ディレクトリ内のファイルの繰り返し - 名前をファイルに保存してから名前を変更します。

ディレクトリ内のファイルの繰り返し - 名前をファイルに保存してから名前を変更します。

タイトルからわかるように、私はbashで次のことを行うプログラムを書くのが好きです。

  • ディレクトリ内のすべてのファイルを繰り返します(各ディレクトリには100個のファイルがあります)。

  • 1から100までの未割り当て乱数を割り当てます。

  • ファイル名をX(割り当てられた乱数)ファイル名の形式で別のファイル(results.txt)に保存します。

  • ファイル名を任意の数字に変更

ファイルを繰り返す方法を知っていますが、残りの部分は私の能力を少し超えています。この問題に対するあなたの助けに非常に感謝します:)

答え1

この練習の最大の難点は、100個のファイルのそれぞれに固有の乱数を生成することです。この問題に対する解決策は、必要な数字(1〜100)を生成し、数字とファイル名を関連付ける前に数字を混在させる(またはファイル名のリストを混在させる)ことです。最後にやりたいことは、100面サイコロを転がしてすでにその数字を転がしていることを確認し、そうであれば、以前に転がっていない数字が出るまでサイコロを振り直すことです。ファイル数が多い場合は、プロセスが必要になる場合があります。非常に長い時間。 (興味深いことに、私たちはこれを数年前にインタビューの質問として使用しました。)

ファイル名に改行文字が含まれていないとします。

paste <( printf '%d\n' {1..100} ) <( printf '%s\n' dir/* | sort -R ) >result.txt

これにより、ユーティリティを使用してタブで区切られた2つの列が作成されますpaste。最初の列には、1から100までの整数が順番に含まれます。 2番目の列には、ディレクトリ内の100個のファイルの名前dir(ディレクトリ名を含む)が含まれています。名前のリストはランダムな順序で並べ替えられます(-Rオプションsortは標準ではありませんが、通常は利用可能です)。

整数を混在させる代わりにファイル名を並べ替えることもできます。

paste <( printf '%d\n' {1..100} | sort -R ) <( printf '%s\n' dir/* ) >result.txt

ファイル名を変更するには、result.txtファイルを読んでください。

while IFS= read -r stuff; do
    number=${stuff%%$'\t'*}   # the thing before the first tab
    pathname=${stuff#*$'\t'}  # the thing after the first tab
    mv -i -- "$pathname" "$(dirname -- "$pathname")/$number"
done <result.txt

現在のディレクトリのすべてのサブディレクトリに対してこのコマンドを実行します(このコマンドを実行する前に2回考えて、重要なデータのバックアップを常に維持してください)。

for dirpath in */; do
    paste <( printf '%d\n' {1..100} | sort -R ) <( printf '%s\n' "$dirpath"/* )
done >result.txt

while IFS= read -r stuff; do
    number=${stuff%%$'\t'*}
    pathname=${stuff#*$'\t'}
    mv -i -- "$pathname" "$(dirname -- "$pathname")/$number"
done <result.txt

あるいはもう少し簡単に言えば、

for dirpath in */; do
    paste <( printf '%d\n' {1..100} | sort -R ) <( printf '%s\n' "$dirpath"/* )
done |
tee result.txt |
while IFS= read -r stuff; do
    number=${stuff%%$'\t'*}
    pathname=${stuff#*$'\t'}
    mv -i -- "$pathname" "$(dirname -- "$pathname")/$number"
done

答え2

この答えは真珠 renameユーティリティ(prenameまたはfile-rename)。 いいえrenameutil-linuxコマンドラインオプションと機能はまったく異なるfromまたは他のrenameコマンドと混同されます。

Perlの名前変更ユーティリティの利点の1つは、比較的単純なsedなどのファイル名変換(たとえばrename 's/foo/bar/' *)を実行できるだけでなく、次のものを使用できることです。どのPerlに実装されているアルゴリズムを使用してファイルの名前を変更します。各ファイル名は Perl の特殊変数に保存され、$_名前が変更されます。そして、もし$_が変更されました。

次の 1 行の名前変更コードが実行する操作は次のとおりです。

$ rename -n '
  BEGIN{
    open(RESULTS,">","results.txt");
  };

  our $rnd = 0;
  our @used;

  # find a random number from 1..100 that hasnt been used yet.
  until (($rnd > 0) && (!defined($used[$rnd]))) {
    $rnd=int(rand(100)+1);
  };
  $used[$rnd]=1;

  print RESULTS "$rnd\t$_\n";
  $_ = $rnd' *

テストを実行するためのオプションとして、-nそれが何をしているかを示します。rename会議許可されている場合は、そうしてください。

実際にファイル名を変更するように削除-n(または詳細な出力に置き換え)してください。-v

ただし、rename名前を変更するファイル名のリストは、コマンドライン引数として指定することも、STDINまたはその両方で指定することもできます。また、注目すべき点は、-0NULで区切られた入力オプション(fromなどfind ... -print0)をサポートすることです。

注:元のファイル名に改行文字が含まれている場合、そのファイルを使用して名前を変更するresults.txtために確実に使用することはできません。\n不明な場合は、改行文字の代わりにNULを使用してresults.txtの各レコードを区切ります。つまり、名前変更スクリプトの最後から2番目の行を次に変更します。

  print RESULTS "$rnd\t$_\0";

参考に反転:

$ rename -n '
  our %files;

  BEGIN{
    open(RESULTS,"<","results.txt");
    # local $/ = "\0"; # uncomment for NUL-separated input
    while(<RESULTS>){
      chomp;
      my ($n,$f) = split /\t/,$_,2;
      $files{$n} = $f;
    };
    close(RESULTS);
  };

  if (defined($files{$_})) { $_ = $files{$_} };
  ' [0-9]*

この,2行はsplit /\t/,$_,2入力レコードに複数のタブがある場合でも分割を最大2つのフィールドに制限するため、ファイル名のタブによってスクリプトが中断されることはありません。

単純な反転により、sed -e 's/^/mv /' results.txt | shスペース、タブなどを含むファイル名またはシェルメタ文字が破損します。


注:私は Kusalananda の回答を読んで、この回答を指摘する価値があると思いました。するd100を繰り返し回して、乱数を使用したことを確認してください。

部分的にはわずか100の乱数がパフォーマンスにほとんどまたはまったく影響を与えず、部分的にはperlのパフォーマンス問題がはるかに小さいので、bashは遅く、bashのループは特に遅いです。しかし、ほとんど彼の答えを読むまで、私はそれについて考えていなかったからです:)

理論的には、このループは無限にすることができます(乱数シーケンスは本当に悪い)、時間がかかるかもしれません。実際には、両方の結果が発生する可能性は非常に低いです。ただし、未使用の数字を生成すると、ループを繰り返すたびに時間がかかることがあります(ただし、人が気付く可能性さえありません)。

List::Utilたとえば、モジュールの機能を使用して、Kの回答などの任意の配列を事前に作成することができますshuffle()

$ rename -n '
  use List::Util qw(shuffle);
  our $i;
  our @random;

  BEGIN{
    @random = shuffle 1..100;
    $i=0;
    open(RESULTS,">","results.txt");
  };

  print RESULTS "$random[$i]\t$_\n";
  $_ = $random[$i++]' *

答え3

そしてzsh

n=0; for f in dir/*(noe['REPLY=$RANDOM']); do
  mv -i -- $f $f:h/$((++n)) &&
    print -r -- $f was renamed to $n
done > result.txt

globoe修飾子は、提供された式(ここでは0から32767の間の任意の数値を返します)の評価に基づいてglobが拡張される順序を定義します。これは、ファイルのように混在した順序を効果的に提供します。

その後、ファイル名を順番に増やす数字に変更し、ディレクトリ内のファイル数に関係なく機能します。

関連情報