Untuk menghindari kondisi balapan :
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
Dan sebagai tambahan, Anda memiliki file yang terbuka untuk ditulis di fd 3.
Dengan bash-4.4+
, Anda dapat membuatnya menjadi fungsi seperti:
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
}
Untuk digunakan misalnya sebagai:
create 3 somefile .ext || exit
printf 'File: "%s"\n' "$REPLY"
echo something >&3
exec 3>&- # close the file
max
value dapat digunakan untuk mencegah loop tak terbatas ketika file tidak dapat dibuat karena alasan lain selain noclobber
.
Perhatikan bahwa noclobber
hanya berlaku untuk >
operator, bukan >>
maupun <>
.
Kondisi balapan yang tersisa
Sebenarnya, noclobber
tidak menghapus kondisi balapan dalam semua kasus. Ini hanya mencegah pukulan biasa file (bukan jenis file lain, sehingga cmd > /dev/null
misalnya tidak gagal) dan memiliki kondisi balapan sendiri di sebagian besar shell.
Shell pertama-tama melakukan stat(2)
pada file untuk memeriksa apakah itu file biasa atau tidak (fifo, direktori, perangkat...). Hanya jika file tersebut tidak ada (belum) atau merupakan file biasa yang melakukan 3> "$file"
gunakan flag O_EXCL untuk menjamin tidak merusak file.
Jadi jika ada file fifo atau perangkat dengan nama itu, itu akan digunakan (asalkan dapat dibuka hanya untuk menulis), dan file biasa dapat dihancurkan jika dibuat sebagai pengganti fifo/perangkat/direktori. .. di antara stat(2)
itu dan open(2)
tanpa O_EXCL!
Mengubah
{ command exec 3> "$file"; } 2> /dev/null
untuk
[ ! -e "$file" ] && { command exec 3> "$file"; } 2> /dev/null
Akan menghindari penggunaan file non-reguler yang sudah ada, tetapi tidak mengatasi kondisi balapan.
Sekarang, itu hanya masalah di hadapan musuh jahat yang ingin membuat Anda menimpa file arbitrer pada sistem file. Itu menghapus kondisi balapan dalam kasus normal dari dua contoh skrip yang sama berjalan pada waktu yang sama. Jadi, lebih baik daripada pendekatan yang hanya memeriksa keberadaan file sebelumnya dengan [ -e "$file" ]
.
Untuk versi yang berfungsi tanpa kondisi balapan sama sekali, Anda dapat menggunakan zsh
shell bukannya bash
yang memiliki antarmuka mentah ke open()
sebagai sysopen
bawaan di 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
Lebih mudah:
touch file`ls file* | wc -l`.ext
Anda akan mendapatkan:
$ ls file*
file0.ext file1.ext file2.ext file3.ext file4.ext file5.ext file6.ext
Skrip berikut dapat membantu Anda. Anda tidak boleh menjalankan beberapa salinan skrip secara bersamaan untuk menghindari kondisi balapan.
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