次の形式でディレクトリをフラット化するにはどうすればよいですか?
前: ./aaa/bbb/ccc.png
後: ./aaa-bbb-ccc.png
./foo/bar/baz.png
、./foo-bar-baz.png
両方が存在する可能性のあるケースをどのように処理するかを指定する必要もあります。後者を前者に置き換えたくないと思いますか?
次の形式でディレクトリをフラット化するにはどうすればよいですか?
前: ./aaa/bbb/ccc.png
後: ./aaa-bbb-ccc.png
./foo/bar/baz.png
、./foo-bar-baz.png
両方が存在する可能性のあるケースをどのように処理するかを指定する必要もあります。後者を前者に置き換えたくないと思いますか?
回答:
警告:これらのコマンドのほとんどをブラウザに直接入力しました。警告講師。
zshおよびzmvの場合:
zmv -o -i -Qn '(**/)(*)(D)' '${1//\//-}$2'
説明:パターン**/*
は、現在のディレクトリのサブディレクトリにあるすべてのファイルに再帰的に一致します(現在のディレクトリにあるファイルには一致しませんが、これらの名前を変更する必要はありません)。括弧の最初の二対として参照されることができる基であり、$1
そして$2
置換テキストに。括弧の最後のペアはD
glob修飾子を追加して、ドットファイルが省略されないようにします。既存のファイルが上書きされるかどうかを尋ねるようにオプション-o -i
を渡すことを意味します。-i
mv
POSIXツールのみ:
find . -depth -exec sh -c '
for source; do
case $source in ./*/*)
target="$(printf %sz "${source#./}" | tr / -)";
mv -i -- "$source" "${target%z}";;
esac
done
' _ {} +
説明:このcase
ステートメントは、現在のディレクトリと現在のディレクトリの最上位のサブディレクトリを省略しています。target
ソースファイル名($0
)が含まれ、先頭の./
文字が取り除かれ、すべてのスラッシュがダッシュに置き換えられ、最後にが含まれz
ます。最後z
は、ファイル名が改行で終わっている場合に存在します。それ以外の場合、コマンド置換はそれを取り除きます。
あなたfind
がサポートしていない場合-exec … +
(OpenBSD、私はあなたを見ています):
find . -depth -exec sh -c '
case $0 in ./*/*)
target="$(printf %sz "${0#./}" | tr / -)";
mv -i -- "$0" "${target%z}";;
esac
' {} \;
bash(またはksh93)では、スラッシュをダッシュに置き換えるために外部コマンドを呼び出す必要はありません。ksh93パラメーター拡張をストリング置換構成で使用できます${VAR//STRING/REPLACEMENT}
。
find . -depth -exec bash -c '
for source; do
case $source in ./*/*)
source=${source#./}
target="${source//\//-}";
mv -i -- "$source" "$target";;
esac
done
' _ {} +
for source
あなたは(通常は)使用している理由としては、最初に提示したファイル/ディレクトリを欠場...私は最終的に出て発見した_
として$0
:) ...と偉大な答えてくれてありがとう。私は彼らから多くを学びます。
ここには良い答えがありますが、これはより直感的ですbash
:
find aaa -type f -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); mv "{}" "$new"' \;
aaa
既存のディレクトリはどこですか。そこに含まれるファイルは現在のディレクトリに移動されます(aaaには空のディレクトリのみが残ります)。tr
ディレクトリとスペースの両方を処理するための2つの呼び出し(エスケープされたスラッシュのロジックなし-読者のための練習)。
これはより直感的で、調整が比較的簡単だと思います。find
つまり、.pngファイルのみを検索する場合は、パラメータを変更できます。tr
通話を変更したり、必要に応じて追加したりできます。そして、それが正しいことを視覚的に確認したい場合は、echo
前に追加できますmv
(次にecho
、物事が正しく見える場合にのみ、追加なしで繰り返します:
find aaa -name \*.png -exec sh -c 'new=$(echo "{}" | tr "/" "-" | tr " " "_"); echo mv "{}" "$new"' \;
また、find aaa
...と...ではなくfind .
... を使用していることに注意してくださいfind aaa/
。後者の2つは、最終的なファイル名に面白いアーティファクトを残すためです。
find . -mindepth 2 -type f -name '*' |
perl -l000ne 'print $_; s/\//-/g; s/^\.-/.\// and print' |
xargs -0n2 mv
注:これは、を含むファイル名では機能しません\n
。
もちろん、これはタイプf
ファイルのみを移動します...
名前の衝突は、pwdに既存のファイルからのみです。
この基本的なサブセットでテスト済み
rm -fr junk
rm -f junk*hello*
mkdir -p junk/junkier/junkiest
touch 'hello hello'
touch 'junk/hello hello'
touch 'junk/junkier/hello hello'
touch 'junk/junkier/junkiest/hello hello'
その結果
./hello hello
./junk-hello hello
./junk-junkier-hello hello
./junk-junkier-junkiest-hello hello
これはls
バージョンです。コメントは歓迎します。このような奇抜なファイル名でも機能します
"j1ळ\n\001\n/j2/j3/j4/\nh h\n"
が、落とし穴を知りたいと思っています。それは「悪意のある人がls
実際に(頑強に)できるか」の練習問題です。
ls -AbQR1p * | # paths in "quoted" \escaped format
sed -n '/":$/,${/.[^/]$/p}' | # skip files in pwd, blank and dir/ lines
{ bwd="$PWD"; while read -n1; # save base dir strip leading "quote
read -r p; do # read path
printf -vs "${p%\"*}" # strip trailing quote"(:)
[[ $p == *: ]] && { # this is a directory
cd "$bwd/$s" # change into new directory
rnp="${s//\//-}"- # relative name prefix
} || mv "$s" "$bwd/$rnp$s"
done; }
ファイル名+パスをtr
コマンドにパイプするだけです。tr <replace> <with>
for FILE in `find aaa -name "*.png"`
do
NEWFILE=`echo $FILE | tr '/' '-'`
cp -v $FILE $NEWFILE
done
cp
行を変更しcp -v "$FILE" "$NEWFILE"
ますか?(また、pngファイルのみを想定するべきではありません。)
cp
行に引用符を追加するだけでは不十分です。ループを使用してfind
出力を解析するアプローチ全体にfor
欠陥があります。使用する唯一の安全な方法がfind
である-exec
、または-print0
(より少ないポータブル可能な場合-exec
)。より堅牢な代替案をお勧めしますが、あなたの質問は現時点では不十分です。
ファイル名のスペースに安全:
#!/bin/bash
/bin/find $PWD -type f | while read FILE
do
_new="${FILE//\//-}"
_new="${_new:1}"
#echo $FILE
#echo $_new
/bin/mv "$FILE" "$_new"
done
明らかな理由でではcp
なくを使って鉱山を走らmv
せましたが、同じものを生産するはずです。
type find
-> find is /usr/bin/find
私のマシンで。PATH
スクリプトで変数名として使用するのはひどい考えです。また、read
コマンドを誤用しているため、スクリプトはファイル名をスペースまたはバックスラッシュでマングルします(このサイトでを検索してくださいIFS read -r
)。
find is hashed (/bin/find)
、関連する方法?異なるディストリビューションは異なりますか?
PATH
環境変数のポイントが不足していることを示唆しているようです。これは、すべての Unixシステムが使用しているものです。あなたが書いた/bin/find
場合、スクリプトは実際にはないすべてのシステム(Gilles、mine、おそらくOP)で壊れています/bin/find
(たとえば、b / cはにあります/usr/bin/find
)。PATH
環境変数の要点は、これを問題にしないことです。
$(pwd)
は変数で既に使用可能です:$PWD
。ただし、ここでは絶対パスを使用しないでください。たとえば、スクリプトがから呼び出された場合、スクリプトは/home/tim
に名前/home/tim/some/path
を変更しようとし/home-tim-some-path
ます。