ファイル形式を含むディレクトリの検索とコピー

ファイル形式を含むディレクトリの検索とコピー

サブディレクトリ「Movie Name」を含む「Movies」ディレクトリがあります。各サブディレクトリ「映画名」には、映画ファイル、関連する画像/情報ファイルなどが含まれています。

「.avi」タイプのムービーファイルを含むすべてのディレクトリを外部USBデバイスにコピーしようとしています。

したがって、Directory/subdirectory Aと が含まれている場合は、そのディレクトリとその内容を外部ドライブにコピーしたいと思います。movie.aviposter.jpgmovie.nfo

私はこれを試しました:

find . -name "*.avi" -printf "%h\n" -exec cp {} /share/USBDisk1/Movies/ \;

ただし、ディレクトリとファイルの内容ではなくファイルのみをコピーします。

答え1

まず、試みが機能しない理由:-printf "%h\n"filenameのディレクトリ部分を印刷してください.avi。これは-exec後続の操作には影響しません。{}「最後に印刷されたコマンド」を意味するのではなく、printf「検出されたファイルへのパス」を意味します。

コマンドでファイル名のディレクトリ部分を使用するには、cpファイル名を渡す必要がありますcpfindコマンドにはこの機能はありませんが、シェルにはあるので、 make がfind呼び出すシェルを呼び出しますcp

find . -name "*.avi" -exec sh -c 'cp -Rp "${0%/*}" /share/USBDisk1/Movies/' {} \;

ディレクトリをコピーするので-r、 。.cp-p

cp -Rpこの方法では、rsync -aムービーディレクトリをすでにコピーしている場合は、内容が変更されない限り再コピーされません。

あなたのコマンドには、あなたに影響を与える可能性があり、そうでない可能性がある欠陥があります。ディレクトリに複数の.aviファイルが含まれている場合は、複数回コピーされます。ファイルを.avi見つけるよりもディレクトリを見つけてファイルが含まれている場合は、コピーすることをお勧めします.avirsync代わりに、ファイルは再コピーされず、ファイルの存在を確認し続けるために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はawkNUL文字を処理できます。
  • 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

関連情報