次のコマンドを使用して、ファイルをサブディレクトリに自動的にグループ化しようとしています。
$ 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).]
この内容がどこに記録されているかはわかりませんが、offset
0から始まり、0
最初の文字も0から始まり、1
2番目の文字も0から始まります。もし offset
null(欠落)の場合は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.jpg
Q.jpg
M/.
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
。rename
mkdir
失敗しても実行されます。 (これは簡単に修正できます。1つの方法はbreak
に変更することですが、exit
エレガントに見えません。)
私はあなたと同じバージョンを持っていませんが、試してみただけでrename
ディレクトリmv M.Butterfly.jpg M/.
にM.Butterfly.jpg
移動しましたM
。したがって、2番目のソリューションを使用している場合、ディレクトリを一覧表示すると、サブ M
ディレクトリ、、、、a
etc(、、、、などのファイルを含む)と 。それからあなたは彼らと一緒に欲しいものは何でもすることができます。 ________________ 1実際、 このコマンドは成功しますが、 と というディレクトリを生成することになりますが、これはおそらく望むものではないでしょう。e
i
o
Many.jpg
Memorable.jpg
Minnesota.jpg
Moments.jpg
M.Butterfly.jpg
M
Q
D
答え3
これはスクリプト内で完全に実行できますrename
。rename
どの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
抽出するにはモジュールを使用する必要があります)。