web-dev-qa-db-ja.com

ファイル名の自動インクリメント

いくつかの重複ファイルを収集する必要があり、名前の衝突を避けたいです。問題は、ファイルがクリーンアップされる前に、このファイルのコレクションがスクリプトの別の実行によって追加され、数を増やし続けたい場合があることです。

そのように数を増やすために、単純なuntilループを決定しました。

until [[ ! -f ${PWD}/DUPES/${num}-$1 ]]; do
num=$((num +1))
done
mv --no-clobber $1 ${PWD}/DUPES/${num}-$1

極端な例では、1k個を超えるファイルを想像することはできないので、私の質問は...

これはこれを達成するためのひどく非効率的な方法ですか?既存のファイルを解析して、そこから増分を開始するための最大の先行番号を取得する必要がありますか、それとも私が提示した極端な例ではこれで問題ありませんか?それとも一緒にもっと良い方法はありますか?

7
akovia

あなたはそれを次のようにするかもしれません:

_set -C; num=0                                  ### set -o noclobber; init $num
[ -e "$1" ] &&                                 ### this needs to be true
until 2>&3 >"./DUPES/$((num+=1))-$1" &&        ### try to open() num+=1
      mv --  "$1"  "./DUPES/$num-$1"           ### if opened, mv over it
do :; done 3>/dev/null                         ### do nothing
_

複数のインスタンスが特定のファイルに対して同じ名前を保護できないことを一度に保証し、変数をインクリメントします。

_/dev/null_ <stderrは、出力の切り捨て/リダイレクトを試みて既存のターゲットを見つけたときに、存在するファイルに関するシェルの苦情を削除するだけです。 noclobberが有効になっている間は、別のファイルは上書きされません-_>|_を使用しない限り、新しいファイルはopen()のみになります。そして、存在しない名前が見つかるまで既存のファイルをインクリメントすることが重要なので、苦情は必要ありません。

パフォーマンスについて-それはwouldゼロから始めなかった方がいいまたは、違いを補おうとした場合。私は上記がいくらか改善されるかもしれないと思います:

_set -C; num=0                                  ### set -o noclobber; init $num
until 2>&3 >"./DUPES/$((num+=1))-$1"  &&       ### try to open() num+=1
      mv --  "$1"  "./DUPES/$num-$1"           ### if opened, mv over it
do    [ -e  "./DUPES/$((num*2))-$1" ] &&       ### halve fail distance
      num=$((num*2))                           ### up to a point of course
done  3>/dev/null                              ### done
_

...しかし、1000までは、おそらくそれについてひどく心配する必要はありません。数秒でランダムな名前で65kに到達しました。

ちなみに、あなたはあなたがただできると思うかもしれません:

_>"./DUPES/$((num+=1))-$1" mv -- "$1" "./DUPES/$num-$1"
_

...しかし、bashシェルでは機能しません。

_num=0; echo >"/tmp/$((num+=1))" >&2 "$num"; echo "$num" /tmp/[01]
_

_0
1 /tmp/1
_

何らかの理由で、bashはリダイレクトのために他のコンテキストで割り当てを行うため、展開は奇妙な順序で行われます。したがって、ここで_$num_を使用して取得したときに、正しい_&&_値を展開するための別の単純なコマンドが必要です。それ以外の場合でも、

_num=0; echo "$((num+=1))" "$num"
_

_1 1
_
5
mikeserv

GNU mvには、便利な--backupオプションがあります。次の相互作用は、ターゲットが存在するときにファイルの名前がどのように変更されるかを示しています。

$ touch a b c o
$ mv --backup=numbered --verbose a o
`a' -> `o' (backup: `o.~1~')
$ mv --backup=numbered --verbose b o
`b' -> `o' (backup: `o.~2~')
$ mv --backup=numbered --verbose c o
`c' -> `o' (backup: `o.~3~')

この例では、元のファイルoo.~1~ao.~2~bo.~3~に名前が変更されています。 cからo。したがって、これは投稿したコードと同じ方法で名前を変更するわけではありませんが、正確なニーズによっては許容できる場合があります。

5
dhag

プロポーザルの2つのインスタンスが同時に実行され、同時に開始されたとします。彼らは両方とも(たとえば)num = 4が使用可能であることを見つけ、同じターゲット名を使用しようとします。

現在、この代替案をテストすることはできませんが、このようなもので十分かもしれません

num=1 attempt=10
while ! mv --no-clobber "$1" "${PWD}/DUPES/${num}-$1" && [[ 0 -lt $attempt ]]
do
    num=$((num+1))
    attempt=$((attempt-1))
done
[[ 0 -eq $attempt ]] && echo "ERROR: Too many attempts to handle $1" >&2
1
roaima