質問
UNIX bash シェル (特に Mac OS X Leopard) で、特定の拡張子を持つすべてのファイルをフォルダー階層 (サブディレクトリを含む) から同じ宛先フォルダー (サブフォルダーなし) にコピーする最も簡単な方法は何でしょうか?
明らかに、ソース階層に重複があるという問題があります。上書きされても構いません。
例:次の階層にあるすべての .txt ファイルをコピーする必要があります
/foo/a.txt
/foo/x.jpg
/foo/bar/a.txt
/foo/bar/c.jpg
/foo/bar/b.txt
「dest」という名前のフォルダーに移動し、次を取得します。
/dest/a.txt
/dest/b.txt
解決
bash の場合:
find /foo -iname '*.txt' -exec cp \{\} /dest/ \;
find
パスの下にあるすべてのファイルが検索されます /foo
ワイルドカードと一致する *.txt
, 、大文字と小文字を区別しない(つまり -iname
手段)。ファイルごとに、 find
実行します cp {} /dest/
, 、見つかったファイルを代わりに使用します {}
.
他のヒント
Magnus のソリューションの唯一の問題は、ファイルごとに新しい「cp」プロセスをフォークすることですが、これは特にファイル数が多い場合にはそれほど効率的ではありません。
Linux (または GNU coreutils を備えた他のシステム) では、次のことができます。
find . -name "*.xml" -print0 | xargs -0 echo cp -t a
(-0 を指定すると、ファイル名にスペースなどの奇妙な文字が含まれている場合でも機能します。)
残念ながら、Mac には BSD スタイルのツールが付属していると思います。「-t」スイッチに相当する「標準」を知っている人はいますか?
質問者はファイルが上書きされることを気にしなかったため、上記の回答では名前の衝突は許可されていません。
ファイルが上書きされるのが気になるので、別の方法を考え出しました。パス内の各 / を - に置き換えると、名前の階層が維持され、すべてのファイルが 1 つのフラット フォルダーに配置されます。
find を使用してすべてのファイルのリストを取得し、次に awk で元のファイル名と変更されたファイル名を使用して mv コマンドを作成し、それらを bash に渡して実行します。
find ./from -type f | awk '{ str=$0; sub(/\.\//, "", str); gsub(/\//, "-", str); print "mv " $0 " ./to/" str }' | bash
ここで、./from と ./to は mv from および to のディレクトリです。
本当に 1 つのコマンドだけを実行したい場合は、コマンドを 1 つ作成して実行してみてはいかがでしょうか。そのようです:
$ find /foo -name '*.txt' | xargs echo | sed -e 's/^/cp /' -e 's|$| /dest|' | bash -sx
ただし、これを頻繁に実行したり、大量のファイルを使用したりしない限り、パフォーマンスの点ではそれほど問題になりません。ただし、名前の共謀には注意してください。テスト中に、GNU cp が少なくとも衝突を警告していることに気づきました。
cp: will not overwrite just-created `/dest/tubguide.tex' with `./texmf/tex/plain/tugboat/tubguide.tex'
最もクリーンだと思います:
$ find /foo -name '*.txt' | xargs -i cp {} /dest
-exec オプションよりも覚えるべき構文が少なくなります。
FreeBSD ボックスの cp のマニュアル ページに関する限り、-t スイッチは必要ありません。3 つ以上の名前が渡された場合、cp はコマンド ラインの最後の引数がターゲット ディレクトリであると想定します。