存在しないターゲットディレクトリを使用したコマンドの名前変更

存在しないターゲットディレクトリを使用したコマンドの名前変更

次のコマンドを使用して、ファイルをサブディレクトリに自動的にグループ化しようとしています。

$ rename 's/(.)(.)(.+)/$1\/$2\/$1$2$3/' *.*

パラメータを使用したテスト実行は、-n私が望むものを示しました。

test.jpg renamed as t/e/test.jpg

しかし、実際の名前の変更は失敗しました。

Can't rename test.jpg t/e/test.jpg: No such file or directory

サブディレクトリがまだ存在していないからです。

まず、すべてのサブディレクトリを手動で作成せずにこれを達成するにはどうすればよいですか?

答え1

名前の変更と他の場所に移動することには違いがあります。この場合、最も簡単な方法(現在bash)はすべてのファイルを繰り返すことです。

for f in *.*
do
    d=${f::1}/${f:1:1}
    [ -d "$d" ] || mkdir -p "$d"
    mv "$f" "$d"
done

説明する

これはbashのよく知られていない「パラメータ拡張/サブ文字列拡張」機能を利用します。

${var:offset:length}

    最大に拡張してください。lengthキャラクターの価値var 指定された文字から始まるoffset

。   [少し義務大きな打撃(1).]

この内容がどこに記録されているかはわかりませんが、offset0から始まり、0最初の文字も0から始まり、12番目の文字も0から始まります。もし offsetnull(欠落)の場合は0として扱われます。

答え2

拡張としてコスタスの答え

for f in *.*
do
    if [ "${f:0:1}" = . ]  ||  [ "${f:1:1}" = . ]
    then
        printf 'Cannot handle "%s"\n' "$f"
    else
        d=${f:0:1}/${f:1:1}
        if ! { [ -d "$d" ] || mkdir -p "$d"  &&  mv "$f" "$d"; }
        then
            break
        fi
    fi
done
  • と同じディレクトリを(実際には)作成できないため、同じファイル名をif 処理するために(最初のエントリ)を追加しました。今回もというディレクトリを作成しようとします。1M.Butterfly.jpgQ.jpgM/.Q/..Dragon.mp4./D

  • &&(  mkdirとの間にmv)を追加してmkdirこれが失敗した場合、ファイルを存在しないディレクトリに移動しようとしません。

  • 2番目のものを追加したのは、if問題が発生した場合はスクリプトを終了し、ユーザーが問題を特定(および修正)できるようにするのが合理的であるためです。
    (たとえば、書き込み権限のないディレクトリで誤ってスクリプトを実行した場合、またはファイルシステムがいっぱいで新しいサブディレクトリを作成できない場合はどうなりますか?)

    のように書くことができます。

    [ -d "$d" ] || mkdir -p "$d"  &&  mv "$f" "$d  ||  break
    

    しかし、これらの&&/||順序はすぐに混乱する可能性があります。

別の変形:

for f in *.*
do
    if [ "${f:0:1}" = . ]  ||  [ "${f:1:1}" = . ]
    then
        printf 'Cannot handle "%s"\n' "$f"
    else
        d=${f:0:1}/${f:1:1}
        if ! { [ -d "$d" ] || mkdir -p "$d"; }          # Note: no "mv" command in the loop.
        then
            break
        fi
    fi
done
rename 's/(.)(.)(.+)/$1\/$2\/$1$2$3/' *.*

これの利点は、数千回でrenameはなく一度だけ呼び出されることです。mvこれにはいくつかの欠点があります。

  • M.Butterfly.jpgと同じファイルQ.jpgがに転送されますrename
  • renamemkdir失敗しても実行されます。 (これは簡単に修正できます。1つの方法は  breakに変更することですが、exitエレガントに見えません。)

私はあなたと同じバージョンを持っていませんが、試してみただけでrenameディレクトリmv M.Butterfly.jpg M/.M.Butterfly.jpg移動しましたM。したがって、2番目のソリューションを使用している場合、ディレクトリを一覧表示すると、サブ  Mディレクトリ、、、、aetc(、、、、などのファイルを含む)と 。それからあなたは彼らと一緒に欲しいものは何でもすることができます。 ________________ 1実際、  このコマンドは成功しますが、 と というディレクトリを生成することになりますが、これはおそらく望むものではないでしょう。eioMany.jpgMemorable.jpgMinnesota.jpgMoments.jpgM.Butterfly.jpg

MQD

答え3

これはスクリプト内で完全に実行できますrenamerenameどのPerlコードは単純な演算子に限定されませんs///

以下は、名前を変更するすべてのファイルが現在のディレクトリにあり、プレフィックスrenameなしでコマンドラインに直接渡される単純な場合に機能します。コマンドラインオプションで始まるファイル名の解釈がある可能性があるため、これは一般的に良い考えではありません。./-rename

または経由renameで渡される場合など、ファイル名にディレクトリプレフィックスが含まれていると機能しません。find./*.*

$ touch test.jpg test.txt
$ rename -n 'my ($a, $b) = (/(.)(.)/);
             mkdir $a unless -e $a;
             mkdir "$a/$b" unless -e "$a/$b";
             if (-e "$a/$b" && ! -d "$a/$b") {
               print STDERR "$_: $a/$b exists but is not a directory!\n";
             } else {;
               s/(.)(.)(.+)/$1\/$2\/$1$2$3/;
             }' *.*
rename(test.jpg, t/e/test.jpg)
rename(test.txt, t/e/test.txt)

より良いバージョンが利用可能ファイル::デフォルト名そしてファイルパスモジュールは両方ともPerlに含まれています。

$ rename -n 'use File::Basename;
             use File::Path qw(make_path);

             my ($bn, $dn) = fileparse($_);
             my ($a, $b) = ($bn =~ /(.)(.)/);
             make_path("$dn$a/$b", { verbose => 1 });
             $_ = "$dn$a/$b/$bn"' ./*.*
mkdir ./t
mkdir ./t/e
rename(./test.jpg, ./t/e/test.jpg)
rename(./test.txt, ./t/e/test.txt)

これにより、各ファイルの元のディレクトリの下に2つのサブディレクトリが作成されます。関数make_path()File::Pathターゲットディレクトリが存在するかどうかを確認します(存在するがディレクトリではない場合は致命的なエラーとして扱われますが、警告のみを印刷し、次のファイル名に進む場合は独自のエラー処理を実装できます)。 )。

必要に$dn応じて、それをハードコードしてすべてのファイルの名前をターゲットの最上位ディレクトリに設定して特定のディレクトリツリーに置き換えることができます(フルパス名からデフォルト名をFile::Basename抽出するにはモジュールを使用する必要があります)。

関連情報