Linux/Bashではなく、同様の質問を見つけました
(ユーザー入力を介して)指定した名前のファイルをスクリプトで作成したいのですが、ファイル名が既に存在する場合は、最後に番号を追加します。
例:
$ create somefile
Created "somefile.ext"
$ create somefile
Created "somefile-2.ext"
次のスクリプトが役立ちます。競合状態を回避するために、スクリプトの複数のコピーを同時に実行しないでください。
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
より簡単に:
touch file`ls file* | wc -l`.ext
あなたは得るでしょう:
$ ls file*
file0.ext file1.ext file2.ext file3.ext file4.ext file5.ext file6.ext
競合状態を回避するには:
_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
_
さらに、fd 3で書き込み用にファイルを開いています。
_bash-4.4+
_を使用すると、次のような関数にすることができます。
_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
}
_
たとえば次のように使用します:
_create 3 somefile .ext || exit
printf 'File: "%s"\n' "$REPLY"
echo something >&3
exec 3>&- # close the file
_
max
値は、noclobber
以外の理由でファイルを作成できない場合に、無限ループを防ぐために使用できます。
noclobber
は_>
_演算子にのみ適用され、_>>
_または_<>
_には適用されないことに注意してください。
実際、noclobber
はすべてのケースで競合状態を削除するわけではありません。 regularファイル(他のタイプのファイルではなく、たとえば、_cmd > /dev/null
_が失敗しないようにする)の破壊を防ぐだけであり、ほとんどのシェルで競合条件自体を持っています。
シェルはまず、ファイルに対してstat(2)
を実行して、それが通常のファイルであるかどうかを確認します(fifo、ディレクトリ、デバイス...)。ファイルが(まだ)存在しないか、通常のファイルである場合にのみ、_3> "$file"
_はO_EXCLフラグを使用して、ファイルが破壊されないことを保証します。
そのため、その名前のfifoファイルまたはデバイスファイルがある場合、それが使用され(書き込み専用で開くことができる場合)、通常のファイルがfifo/device/directoryの代わりとして作成された場合、それが破壊される可能性があります。 ..そのstat(2)
とopen(2)
の間のO_EXCLなし!
の変更
_ { command exec 3> "$file"; } 2> /dev/null
_
に
_ [ ! -e "$file" ] && { command exec 3> "$file"; } 2> /dev/null
_
既存の非標準ファイルの使用は避けますが、競合状態には対処しません。
さて、それは、ファイルシステム上の任意のファイルを上書きさせようとする悪意のある攻撃者が直面した場合にのみ、本当に懸念事項です。同じスクリプトの2つのインスタンスが同時に実行されるという通常のケースでは、競合状態が解消されます。したがって、その点では、_[ -e "$file" ]
_を使用して事前にファイルの存在のみをチェックするアプローチよりも優れています。
競合状態がまったくない作業バージョンの場合、zsh
の代わりにbash
シェルを使用できます。これは、sysopen
としてopen()
への未加工インターフェースを持っています_zsh/system
_モジュールに組み込み:
_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
_
このようなものを試してください
name=somefile
path=$(dirname "$name")
filename=$(basename "$name")
extension="${filename##*.}"
filename="${filename%.*}"
if [[ -e $path/$filename.$extension ]] ; then
i=2
while [[ -e $path/$filename-$i.$extension ]] ; do
let i++
done
filename=$filename-$i
fi
target=$path/$filename.$extension
touch
の代わりにecho
または必要なものを使用します。
_echo file$((`ls file* | sed -n 's/file\([0-9]*\)/\1/p' | sort -rh | head -n 1`+1))
_
説明された表現の一部:
ls file*
_sed -n 's/file\([0-9]*\)/\1/p'
sort -rh
_head -n 1
_次のようなものを試してみてください(テストされていませんが、アイデアはわかります)。
filename=$1
# If file doesn't exist, create it
if [[ ! -f $filename ]]; then
touch $filename
echo "Created \"$filename\""
exit 0
fi
# If file already exists, find a similar filename that is not yet taken
digit=1
while true; do
temp_name=$filename-$digit
if [[ ! -f $temp_name ]]; then
touch $temp_name
echo "Created \"$temp_name\""
exit 0
fi
digit=$(($digit + 1))
done
何をしているのかに応じて、touch
の呼び出しを、作業中のファイルを作成するために必要なコードに置き換えます。
これは、ディレクトリを段階的に作成するために使用したはるかに優れた方法です。
ファイル名についても調整できます。
LAST_SOLUTION=$(echo $(ls -d SOLUTION_[[:digit:]][[:digit:]][[:digit:]][[:digit:]] 2> /dev/null) | awk '{ print $(NF) }')
if [ -n "$LAST_SOLUTION" ] ; then
mkdir SOLUTION_$(printf "%04d\n" $(expr ${LAST_SOLUTION: -4} + 1))
else
mkdir SOLUTION_0001
fi
一般化された関数としての チョロバの答え の単純な再パッケージ:
autoincr() {
f="$1"
ext=""
# Extract the file extension (if any), with preceeding '.'
[[ "$f" == *.* ]] && ext=".${f##*.}"
if [[ -e "$f" ]] ; then
i=1
f="${f%.*}";
while [[ -e "${f}_${i}${ext}" ]]; do
let i++
done
f="${f}_${i}${ext}"
fi
echo "$f"
}
touch "$(autoincr "somefile.ext")"