GNU/Linux >> Linux の 問題 >  >> Linux

新しいファイルを作成しますが、ファイル名が bash に既に存在する場合は番号を追加します

競合状態を回避するには :

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 すべてのケースで競合状態が解消されるわけではありません。 通常の破壊を防ぐだけです ファイル (他の種類のファイルではないため、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 の代わりにシェル open() への生のインターフェースを持っています sysopen のように 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

簡単:

touch file`ls file* | wc -l`.ext

以下が得られます:

$ ls file*
file0.ext  file1.ext  file2.ext  file3.ext  file4.ext  file5.ext  file6.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

Linux
  1. Bash で、ファイルの各行の後に文字列を追加するにはどうすればよいですか?

  2. BASH で mv を使用してファイル名にタイムスタンプを追加する

  3. ファイルを bash シェル スクリプトに含める方法

  1. bash プロンプトにアイコンを追加する方法

  2. ファイルの行数だけを取得する方法

  3. ジェンキンスは死んでいますが、pidファイルは存在します

  1. アトミック作成ファイルが存在しない場合は、bash スクリプトから

  2. まだファイルにない場合にのみ、ファイルに行を追加します

  3. <service-name> は停止していますが、pid ファイルは存在します