Um die Rennbedingungen zu vermeiden :
name=some-file
n=
set -o noclobber
until
file=$name${n:+-$n}.ext
{ command exec 3> "$file"; } 2> /dev/null
do
((n++))
done
printf 'File is "%s"\n' "$file"
echo some text in it >&3
Und zusätzlich haben Sie die Datei zum Schreiben auf fd 3 geöffnet.
Mit bash-4.4+
, können Sie daraus eine Funktion machen wie:
create() { # fd base [suffix [max]]]
local fd="$1" base="$2" suffix="${3-}" max="${4-}"
local n= file
local - # ash-style local scoping of options in 4.4+
set -o noclobber
REPLY=
until
file=$base${n:+-$n}$suffix
eval 'command exec '"$fd"'> "$file"' 2> /dev/null
do
((n++))
((max > 0 && n > max)) && return 1
done
REPLY=$file
}
Zum Beispiel zu verwenden als:
create 3 somefile .ext || exit
printf 'File: "%s"\n' "$REPLY"
echo something >&3
exec 3>&- # close the file
Die max
Wert kann zum Schutz vor Endlosschleifen verwendet werden, wenn die Dateien aus anderen Gründen als noclobber
nicht erstellt werden können .
Beachten Sie, dass noclobber
gilt nur für >
Operator, nicht >>
noch <>
.
Verbleibende Rennbedingung
Eigentlich noclobber
entfernt die Racebedingung nicht in allen Fällen. Es verhindert nur das regelmäßige Klobben Dateien (keine anderen Dateitypen, sodass cmd > /dev/null
schlägt beispielsweise nicht fehl) und hat in den meisten Shells selbst eine Race-Bedingung.
Die Shell macht zuerst einen stat(2)
auf die Datei, um zu prüfen, ob es sich um eine reguläre Datei handelt oder nicht (fifo, Verzeichnis, Gerät ...). Nur wenn die Datei (noch) nicht existiert oder eine normale Datei ist, wird 3> "$file"
ausgeführt Verwenden Sie das O_EXCL-Flag, um sicherzustellen, dass die Datei nicht geclobbt wird.
Wenn es also eine Fifo- oder Gerätedatei mit diesem Namen gibt, wird sie verwendet (vorausgesetzt, sie kann nur im Schreibmodus geöffnet werden), und eine normale Datei kann geclobbered werden, wenn sie als Ersatz für ein Fifo/Gerät/Verzeichnis erstellt wird. .. dazwischen stat(2)
und open(2)
ohne O_EXCL!
Ändern der
{ command exec 3> "$file"; } 2> /dev/null
zu
[ ! -e "$file" ] && { command exec 3> "$file"; } 2> /dev/null
Würde die Verwendung einer bereits vorhandenen nicht regulären Datei vermeiden, aber die Race-Bedingung nicht ansprechen.
Nun, das ist nur angesichts eines böswilligen Angreifers wirklich ein Problem, das Sie dazu bringen möchte, eine beliebige Datei im Dateisystem zu überschreiben. Es entfernt die Racebedingung im Normalfall, wenn zwei Instanzen desselben Skripts gleichzeitig ausgeführt werden. Insofern also besser als Ansätze, die vorher nur mit [ -e "$file" ]
auf Dateiexistenz prüfen .
Für eine funktionierende Version ohne Race Condition könnten Sie den zsh
verwenden Shell anstelle von bash
die eine rohe Schnittstelle zu open()
hat als sysopen
eingebaut in zsh/system
Modul:
zmodload zsh/system
name=some-file
n=
until
file=$name${n:+-$n}.ext
sysopen -w -o excl -u 3 -- "$file" 2> /dev/null
do
((n++))
done
printf 'File is "%s"\n' "$file"
echo some text in it >&3
Einfacher:
touch file`ls file* | wc -l`.ext
Sie erhalten:
$ ls file*
file0.ext file1.ext file2.ext file3.ext file4.ext file5.ext file6.ext
Das folgende Skript kann Ihnen dabei helfen. Sie sollten nicht mehrere Kopien des Skripts gleichzeitig ausführen, um eine Race-Condition zu vermeiden.
name=somefile
if [[ -e $name.ext || -L $name.ext ]] ; then
i=0
while [[ -e $name-$i.ext || -L $name-$i.ext ]] ; do
let i++
done
name=$name-$i
fi
touch -- "$name".ext