競合なしでファイル名番号を移動するには?

競合なしでファイル名番号を移動するには?

次のファイルのリストを検討してください。

$ touch index-{1,2,3,4,5,6,7,8,9}.txt

変えたいなら下にだから彼らは最初から始まります。比較的簡単:

$ rename --verbose 's/^index-([1-9])\.txt$/$1/; $_="index-" . ($_ - 1) . ".txt"' index-*.txt

man bashこれは、指定されたglobがアルファベット順にソートされ、次のように名前が変更されるために機能しますindex-1.txtindex-0.txt 今後index-2.txtに名前が変更されましたindex-1.txt

変更しようとすると失敗します。戻る、または番号が次の場合異なる長さ:

$ touch index-{10,11}.txt
$ rename --verbose 's/^index-([0-9]+)\.txt$/$1/; $_="index-" . ($_ + 1) . ".txt"' index-*.txt
index-10.txt not renamed: index-11.txt already exists
index-11.txt renamed as index-12.txt
index-1.txt not renamed: index-2.txt already exists

可能な長期修正:

  • rename競合するオプションが発生しなくなるまで作業順序を再指定してください。
  • renameまず、ファイルを一時ディレクトリに移動してから新しい名前に戻すオプションです。
  • rename最初のステップでは、元の名前や新しい名前と競合しない一意の名前を使用して2つのステップにファイル名を変更するオプションです。
  • 自然なアライメントをする方法+裏返すBash グローバル変数。

この問題を解決する簡単な方法はありますか?

ベストアンサー1

一連の名前変更がどの順序で行われるべきかを決定するための一般的な解決策を見つけるのに役立つかもしれないUnixには、よく知られていない標準コマンドがあります。tsort

次のファイルに実行する名前変更のリストがあるとしますrenames.txt(実演のために、その名前にスペースが含まれていないとします)。

d a
e f
b e
a c

d名前が変更されるため、まず名前を変更する必要があるaという意味です。したがって、ファイル名を変更する必要がある順序とは逆の部分ソート順序があります。ad

tsortソート順の部分リストからソート順全体を推測するツールです。ループがあるとエラーが返されるため、解決策がない状況を検出するのに役立ちます。その入力を適用すると、tsort次のようになります。

b
d
e
a
f
c

後に名前を変更するb必要があることがわかります。私達は利用できますde牛に似た一種の栄養 tac(一部のシステムでも利用可能tail -r)順序を逆にするには:

c
f
a
e
d
b

そして、名前変更リストに追加してください。

tsort renames.txt | tac | awk '
  NR==FNR {
    ren[$1] = $2
    next
  }
  $1 in ren {
    print "mv -i --", $1, ren[$1]
  }' renames.txt -

これは私たちに以下を提供します。

mv -i -- a c
mv -i -- e f
mv -i -- d a
mv -i -- b e

その後、パイプラインを介して実行できますsh

ただし、上記のコードはループを検出するために上記の終了状態を確認しておらず、tsortファイル名に特殊なシェル文字を含めないでください。

これ堅牢性読者のための練習として残しました;-)

おすすめ記事