![파일 형식이 포함된 디렉터리 찾기 및 복사](https://linux55.com/image/31849/%ED%8C%8C%EC%9D%BC%20%ED%98%95%EC%8B%9D%EC%9D%B4%20%ED%8F%AC%ED%95%A8%EB%90%9C%20%EB%94%94%EB%A0%89%ED%84%B0%EB%A6%AC%20%EC%B0%BE%EA%B8%B0%20%EB%B0%8F%20%EB%B3%B5%EC%82%AC.png)
하위 디렉터리 "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
(왜냐하면 둘 다 복사하고 싶지 않고./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