サブディレクトリ「Movie Name」を含む「Movies」ディレクトリがあります。各サブディレクトリ「映画名」には、映画ファイル、関連する画像/情報ファイルなどが含まれています。
「.avi」タイプのムービーファイルを含むすべてのディレクトリを外部USBデバイスにコピーしようとしています。
したがって、Directory/subdirectory A
と が含まれている場合は、そのディレクトリとその内容を外部ドライブにコピーしたいと思います。movie.avi
poster.jpg
movie.nfo
私はこれを試しました:
find . -name "*.avi" -printf "%h\n" -exec cp {} /share/USBDisk1/Movies/ \;
ただし、ディレクトリとファイルの内容ではなくファイルのみをコピーします。
答え1
まず、試みが機能しない理由:-printf "%h\n"
filenameのディレクトリ部分を印刷してください.avi
。これは-exec
後続の操作には影響しません。{}
「最後に印刷されたコマンド」を意味するのではなく、printf
「検出されたファイルへのパス」を意味します。
コマンドでファイル名のディレクトリ部分を使用するには、cp
ファイル名を渡す必要がありますcp
。find
コマンドにはこの機能はありませんが、シェルにはあるので、 make がfind
呼び出すシェルを呼び出しますcp
。
find . -name "*.avi" -exec sh -c 'cp -Rp "${0%/*}" /share/USBDisk1/Movies/' {} \;
ディレクトリをコピーするので-r
、 。.cp
-p
cp -Rp
この方法では、rsync -a
ムービーディレクトリをすでにコピーしている場合は、内容が変更されない限り再コピーされません。
あなたのコマンドには、あなたに影響を与える可能性があり、そうでない可能性がある欠陥があります。ディレクトリに複数の.avi
ファイルが含まれている場合は、複数回コピーされます。ファイルを.avi
見つけるよりもディレクトリを見つけてファイルが含まれている場合は、コピーすることをお勧めします.avi
。rsync
代わりに、ファイルは再コピーされず、ファイルの存在を確認し続けるためにcp
さらに多くの作業が必要になります。rsync
すべてのムービーディレクトリが最上位ディレクトリのすぐ下にある場合、これは必要ではなく、find
ワイルドカードパターンの単純なループで十分です。
for d in ./*/; do
set -- "$d/"*.avi
if [ -e "$1" ]; then
# there is at least one .avi file in $d
cp -rp -- "$d" /share/USBDisk1/Movies/
fi
done
ムービーディレクトリがネストされている場合(たとえばMovies/science fiction/Ridley Scott/Blade Runner
、)これは必要ではなく、find
ワイルドカードパターンの単純なループで十分です。 ksh93またはbash≥4またはzshを実行し、ksh93では最初に実行し、bashでは最初に実行する必要がありset -o globstar
ます。shopt -s globstar
ワイルドカードパターンは、**/
現在のディレクトリとすべてのサブディレクトリを繰り返し一致させます(bashはサブディレクトリへのシンボリックリンクも通過します)。
for d in ./**/; do
set -- "$d/"*.avi
if [ -e "$1" ]; then
cp -rp -- "$d" /share/USBDisk1/Movies/
fi
done
答え2
GNUツールを使用しているようですので、次のようにすることができます。
find . -name '*.avi' -printf '%h\0' |
tr '\1/' '/\1' |
LC_ALL=C sort -zu |
tr '\1/' '/\1' |
awk -vRS='\0' -vORS='\0' '
NR>1 && substr($0, 1, length(l)) == l {next}
{print; l=$0"/"}' |
xargs -r0 cp -rt /share/USBDisk1/Movies/
上記はGNUにのみ適用されます。
find
(ため-printf
)- なぜなら
sort
-z
- forは
awk
NUL文字を処理できます。 - for
xargs
(-r
および-0
オプション、一部のBSDもサポートしていますが) cp
(のため-t
の)
アイデアは次のとおりです。
find
すべてのファイルのディレクトリ名を出力しますavi
。- 各ディレクトリが親ディレクトリの後ろにリストされるようにリストを並べ替えます(対応する
/
文字を他のディレクトリの前にソートする必要があるために置き換えます\1
)。 sort -u
各ディレクトリが一度だけ表示されるようにこれを使用します。- 私たちはすでに祖先を含むすべてのディレクトリエントリを削除するために小さなスクリプトを使用します(
awk
なぜなら、両方をコピーしたくなく、両方ともAVIファイルが含まれているからです)。./a
./a/b
xargs
に引数として渡されるように、そのリストをstdinに渡しますcp -t target
。
一部のディレクトリの名前が同じ場合は、潜在的な競合を回避しようとしないことに注意してください。
ターゲットドライブのディレクトリ構造を再現するには、xargs -r0 cp -rt /share/USBDisk1/Movies/
次のようにします。
pax -0rw /share/USBDisk1/Movies/
(少なくともpax
このオプションはDebianまたはMirBSDでサポートされています)。-0
理想的には、次のように書くことができると思います。
find . -type d -has '*.avi' -prune -exec cp -rt /target {} +
残念ながら、find
そのような述語はありません-has
。達成できる最も近いものは次のとおりです。
find . -type d -exec bash -c '
shopt -s nullglob dotglob
set -- "$1"/*.avi
(( $# > 0 ))' sh {} \; \
-prune -exec cp -r {} /share/USBDisk1/Movies/ \;
ただし、これは1つを呼び出してbash
各ディレクトリのファイルリストを再実行することを意味するbash
ため、以前のソリューションよりも効率が悪くなる可能性があります。
答え3
私はこれがもっと簡単で必要なことをほとんどやっていると思います。
find . -name "*.avi" -exec cp "{}" /share/USBDisk1/Movies/ \;
私はそれがあなたがやっていることに似ていると思いますが、findの出力を-printf
キャプチャしません。"{}"
引用:https://stackoverflow.com/questions/16898462/moving-files-from-directory-and-all-subdirectories
最高、
答え4
xargs
以下を使用してcp
これを行うことができます。
find . -name "*.avi" -printf "%h\n" | xargs cp -t /share/USBDisk1/Movies/ -r