mvファイルを正規表現置換によって生成された別のパスにコピーする

mvファイルを正規表現置換によって生成された別のパスにコピーする

ファイル名に基づいてサブディレクトリに格納されているファイルでいっぱいのディレクトリがあります。

20d1/d325/52d1/20d1d32552d1a95249e62662fbdf924dd72c4027.jpg
ccaf/13cf/3199/ccaf13cf319930e80f5f2ad02525b93e1326c160.jpg
ec07/53bd/2355/ec0753bd2355fa8ec5cf5163e219c162cce3b03a.jpg
...

ご覧のとおり、ファイル名の最初の12文字は3つのレベルのサブディレクトリを作成するために使用されます。残念ながら、各ディレクトリ名に4文字が選択され、ファイル数がファイルシステムのディレクトリあたりのエントリ数である32,000のディレクトリ制限を超えました。したがって、次のように書き直す必要があります。

20d/1d3/255/2d1/20d1d32552d1a95249e62662fbdf924dd72c4027.jpg
cca/f13/cf3/199/ccaf13cf319930e80f5f2ad02525b93e1326c160.jpg
ec0/753/bd2/355/ec0753bd2355fa8ec5cf5163e219c162cce3b03a.jpg
...

したがって、ディレクトリごとに4つではなく3つの文字が使用されます。ファイルが多いため、プロセスはできるだけ早くする必要があります。

私は次のことを試しましたfind

find /path/to/files -mindepth 4 -type f -regextype posix-extended -regex \
".*/([0-9a-f]{4}/){3}(([0-9a-f]{3})([0-9a-f]{3})([0-9a-f]{3})([0-9a-f]{3})[0-9a-f]+\.\w+)" 

これにより、すべてのファイルが正しく印刷されますが、書き換えを進める方法がわかりません。書き換えプロセス中に正規表現キャプチャグループを使用してパス$3/$4/$5/$6/$2find正規表現の逆参照)を再構築したいと思います。しかし、findこのようなものはサポートされていないようです。

find ... -exec cp {} /elsewhere/$3/$4/$5/$6/$2 ;

この問題を処理する最良の方法は何ですか?そして(私はこれについて経験がありません)sedのいくつかの組み合わせですか?タスクを実行するxargs代わりに繰り返す必要がありますか?find少し迷子になった。

答え1

ファイルをコピーするには、findとGNU tarの組み合わせを使用して操作を実行できます。

$ find -type f ... -print0 \
    | tar -c -f - --null --files-from - \
    | tar -C DEST_BASE -v -x -f - \
        --show-transformed \
        --transform 's,PATTERN,REPLACE,OPTIONS

(findはすべてのソースファイル名を生成し、最初のtarはそれをパイプに読み込み、2番目のtarはファイル名/パス変換を実行します)

デフォルトでは、この--transformオプションにはデフォルトの正規表現が必要ですが、xregexp-optionを使用することもできます。別の有用な正規表現オプションは、大文字とi小文字を区別しない一致です。

答え2

モバイルでは利用可能mmv:

$ mmv -n ';????????????*.jpg' '#2#3#4/#5#6#7/#8#9#10/#11#12#13/#14.jpg'
20d1/d325/52d1/20d1d32552d1a95249e62662fbdf924dd72c4027.jpg
    -> 20d/1d3/255/2d1/a95249e62662fbdf924dd72c4027.jpg
ccaf/13cf/3199/ccaf13cf319930e80f5f2ad02525b93e1326c160.jpg
    -> cca/f13/cf3/199/30e80f5f2ad02525b93e1326c160.jpg
ec07/53bd/2355/ec0753bd2355fa8ec5cf5163e219c162cce3b03a.jpg
    -> ec0/753/bd2/355/fa8ec5cf5163e219c162cce3b03a.jpg

(-nはレポートとテストのみです。ファイルはまだ実際には移動されていません。)

残念ながらmmv、「見つからないディレクトリを作成する」オプションがないため、実際に移動する前にこれを行う必要があります。

$ mmv -n ';????????????*.jpg' '#2#3#4/#5#6#7/#8#9#10/#11#12#13/#14.jpg' \
    | sed 's,^.* -> \(.*/\)[^/]\+$,\1,' \
    | xargs mkdir -p

mmv正規表現ではなくシェルワイルドカードを使用してください。この;文字は特別で、ソースファイルのデフォルトパスと一致します。逆参照はで表示されます#n。シェルワイルドカードは拡張正規表現ほど強力ではないため、?ファイル名の最初の12文字を一致させるために12のワイルドカードを使用しました。

答え3

ファイルが多いため、コマンドラインの長さの制限を知っておく必要があります。また、パフォーマンス上の理由から、各ファイルに対して新しいプロセスを開始しないことをお勧めします。

ファイルをコピーしないでください。時間がかかり、ディスク容量が2倍になり、コピーを削除せずに元のファイルのみを削除するという問題が発生します。ファイルの移動ははるかに安定しています。

これはシェルユーティリティを使用して行うことができますが、Perl、Python、またはRubyで強力で効率的なスクリプトを書くのははるかに簡単です。引用に問題がないか、コマンドラインを分割する必要はありません。

Perl(ディレクトリ削除時のエラーチェックを省略):

#!/usr/bin/env perl
use warnings;
for my $dir1 (<*>) {
    for my $dir2 (<$dir1/*>) {
        for my $dir3 (<$dir2/*>) {
            for my $file (<$dir3/*>) {
                $file =~ m:.*/((...)(...)(...)(...).*):;
                mkdir "$1";
                mkdir "$1/$2";
                mkdir "$1/$2/$3";
                mkdir "$1/$2/$3/$4";
                rename $file, "$1/$2/$3/$4/$file" or die "$file: $!";
            }
            rmdir $dir3;
        }
        rmdir $dir2;
    }
    rmdir $dir1;
}

関連情報