파일 형식이 포함된 디렉터리 찾기 및 복사

파일 형식이 포함된 디렉터리 찾기 및 복사

하위 디렉터리 "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파일 이름을 전달해야 합니다 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는 awkNUL 문자를 처리할 수 있습니다.
  • for xargs( -r-0옵션, 일부 BSD도 지원하지만)
  • cp(을 위한 -t)

아이디어는 다음과 같습니다.

  • find모든 파일의 디렉토리 이름을 출력합니다 avi.
  • 각 디렉토리가 상위 디렉토리 뒤에 나열되도록 목록을 정렬합니다(해당 /문자를 다른 디렉토리보다 먼저 정렬해야 하기 때문에 로 교체합니다 \1).
  • sort -u각 디렉토리가 한 번만 나타나도록 이것을 사용합니다 .
  • 우리는 이미 조상을 포함하고 있는 모든 디렉토리 항목을 삭제하기 위해 작은 스크립트를 사용합니다 awk(왜냐하면 둘 다 복사하고 싶지 않고 ./a./a/b다 AVI 파일을 포함하고 있기 때문입니다).
  • 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/ \;

그러나 이는 하나를 호출 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

관련 정보