38

同様の質問が見つかりましたが、Linux/Bashでは見つかりませんでした

スクリプトで(ユーザー入力を介して)指定された名前のファイルを作成したいのですが、ファイル名がすでに存在する場合は最後に番号を追加します。

例:

$ create somefile
Created "somefile.ext"
$ create somefile
Created "somefile-2.ext"
4

9 に答える 9

41

次のスクリプトが役立ちます。競合状態を回避するために、スクリプトの複数のコピーを同時に実行しないでください。

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
于 2012-08-29T23:33:44.327 に答える
14

より簡単に:

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

あなたが得るでしょう:

$ ls file*
file0.ext  file1.ext  file2.ext  file3.ext  file4.ext  file5.ext  file6.ext
于 2018-04-05T11:33:00.260 に答える
9

競合状態を回避するには

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

さらに、fd3に書き込むためにファイルを開いています。

を使用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代わりにシェルを使用できます。bashopen()sysopenzsh/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
于 2012-08-30T10:05:47.207 に答える
6

このようなものを試してください

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
于 2016-02-15T06:48:02.557 に答える
5

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
  • すべてをパイプで結合して増分します(上記の完全な式)
于 2018-05-10T07:32:51.263 に答える
2

このようなものを試してみてください(テストされていませんが、アイデアは得られます):

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実行している内容に応じて、の呼び出しを、操作しているファイルを作成するために必要なコードに置き換えます。

于 2012-08-29T23:33:32.320 に答える
1

これは、ディレクトリを段階的に作成するために使用したはるかに優れた方法です。

ファイル名に合わせて調整することもできます。

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
于 2014-02-27T01:45:36.703 に答える
0

一般化された関数としてのchorobaの答えの単純な再パッケージ化:

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")"
于 2016-10-14T17:39:42.177 に答える
0

ループせず、正規表現やシェルexprを使用しません。

last=$(ls $1* | tail -n1)
last_wo_ext=$($last | basename $last .ext)
n=$(echo $last_wo_ext | rev | cut -d - -f 1 | rev)
if [ x$n = x ]; then
    n=2
else
    n=$((n + 1))
fi
echo $1-$n.ext

「-1」の拡張子と例外なしでより単純です。

n=$(ls $1* | tail -n1 | rev | cut -d - -f 1 | rev)
n=$((n + 1))
echo $1-$n.ext
于 2020-12-27T21:45:14.467 に答える