Aby se vyhnuli podmínkám závodu :
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
A navíc máte soubor otevřený pro zápis na fd 3.
S bash-4.4+
, můžete z něj vytvořit funkci jako:
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
}
K použití například jako:
create 3 somefile .ext || exit
printf 'File: "%s"\n' "$REPLY"
echo something >&3
exec 3>&- # close the file
max
hodnotu lze použít k ochraně před nekonečnými smyčkami, když soubory nelze vytvořit z jiného důvodu než noclobber
.
Všimněte si, že noclobber
platí pouze pro >
operátor, nikoli >>
ani <>
.
Zbývající závodní stav
Vlastně noclobber
neodstraní spor ve všech případech. Zabraňuje pouze pravidelnému ucpání soubory (nikoli jiné typy souborů, takže cmd > /dev/null
například neselže) a má ve většině shellů samotný závod.
Shell nejprve provede stat(2)
na souboru zkontrolovat, zda se jedná o běžný soubor nebo ne (fifo, adresář, zařízení...). Pouze v případě, že soubor (zatím) neexistuje nebo jde o běžný soubor, 3> "$file"
použijte příznak O_EXCL, abyste zaručili, že soubor nebude clobován.
Pokud tedy existuje soubor fifo nebo zařízení s tímto názvem, bude použit (za předpokladu, že jej lze otevřít pouze pro zápis), a běžný soubor může být zablokován, pokud bude vytvořen jako náhrada za fifo/zařízení/adresář. .. mezi tím stat(2)
a open(2)
bez O_EXCL!
Změna
{ command exec 3> "$file"; } 2> /dev/null
do
[ ! -e "$file" ] && { command exec 3> "$file"; } 2> /dev/null
Vyhnulo by se použití již existujícího neobvyklého souboru, ale neřešilo spor.
Nyní je to opravdu problém tváří v tvář zlomyslnému protivníkovi, který by vás chtěl přimět přepsat libovolný soubor v systému souborů. Odstraní spor v normálním případě dvou instancí stejného skriptu spuštěného ve stejnou dobu. V tom je tedy lepší než přístupy, které pouze předem ověřují existenci souboru pomocí [ -e "$file" ]
.
Pro pracovní verzi bez sporu můžete použít zsh
shell místo bash
který má nezpracované rozhraní na open()
jako sysopen
vestavěný v 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
Jednodušší:
touch file`ls file* | wc -l`.ext
Získáte:
$ ls file*
file0.ext file1.ext file2.ext file3.ext file4.ext file5.ext file6.ext
Následující skript vám může pomoci. Neměli byste spouštět několik kopií skriptu současně, abyste se vyhnuli sporu.
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