web-dev-qa-db-ja.com

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

Linux/Bashではなく、同様の質問を見つけました

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

例:

$ create somefile
Created "somefile.ext"
$ create somefile
Created "somefile-2.ext"
26
heltonbiker

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

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
33
choroba

より簡単に:

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

あなたは得るでしょう:

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

さらに、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
_
7

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

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
5
Gyuha Shin

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_
  • すべてをパイプとインクリメントで組み合わせる(上記の完全な式)
3
Serg Stetsuk

次のようなものを試してみてください(テストされていませんが、アイデアはわかります)。

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の呼び出しを、作業中のファイルを作成するために必要なコードに置き換えます。

2
bta

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

ファイル名についても調整できます。

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
1
Oliver

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

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")"
0
SamWN