find . -exec cmd {} +
find . -print0 | xargs -0 cmd
둘 다 find로 찾은 파일에 대해 명령을 실행하는 안정적인 방법입니다.
어느 것이 선호됩니까? 어느 것이 더 휴대 가능하고 안정적이며 효율적이고 다재다능하며 그 이유는 무엇입니까?
답변1
긴 이야기 짧게
확실한 승자는 없습니다. 내 제안은 다음을 사용하는 것입니다.
find . -exec cmd {} +
이식성이 뛰어나고 리소스를 덜 사용하며 문제가 적고 다음 중 하나를 충족하면 충분합니다.
xargs -r0 -other-options -a <(find ... -print0 | ...) cmd
find . -print0 | ... | xargs -0 -other-options cmd
다른 도구의 추가 기능이 필요하거나 xargs
다른 도구의 출력을 후처리해야 find
하고 현재 사용 중인 시스템이 이러한 비표준 옵션을 지원하며 이러한 제한 사항이 적용되지 않거나 무시할 수 있다는 것을 알고 있는 경우.
역사
find
이미 -exec cmd {} ';'
이 변종은
cmd
파일당 단일 호출을 실행하고 조건부 술어로도 작동합니다. 70년대 중반 Unix V5에서 현재 인터페이스로 다시 구현되었지만 이 -exec cmd {} +
형식은 여러 파일을 에 전달합니다 cmd
. . David Korn이 작성했으며 1988년 System V 릴리스 4에서 처음 출시되었지만(참고자료 참조 lynx news://news.gmane.io/gmane.comp.standards.posix.austin.general/2192
) SVR4.2(1992)까지는 문서화되지 않았습니다.
이것은 단지POSIX 표준 2001 버전에 추가됨일부 구현에서는 find
나중에 추가했습니다(4.2.12, GNU 2005
find
, FreeBSD 2002, NetBSD 2006, busybox 2015).
xargs
그 자체는 1970년대 후반 PWB Unix에서 유래되었습니다. 이상하고 불필요한 기능과 제한이 있는 매우 열악한 인터페이스를 가지고 있었고 (아직도 가지고 있습니다) 독특한 형식의 인용을 이해합니다(비록 아직 살아남지 못한 PWB Unix 쉘이 이해하는 것과 상당히 가깝지만). 그 목적은 출력을 처리하는 것이지만 find
이를 안정적으로 수행할 수는 없습니다.
-0
1990년에 GNU에 옵션이 추가되면서 xargs
새로운 -print0
옵션이 추가되었습니다.
find
GNU 작성자가 옵션을 추가했을 때 SysV에 대해 몰랐던 것이 확실한 것입니다 find
. 그 이후로 NUL로 구분된 문제를 처리하기 위해 -exec {} +
일부
-0
/// --null
옵션이 다른 GNU 유틸리티에 점차 추가되었습니다.-z
--zero
교환모든 파일 경로와 더 일반적으로 모든 C 문자열 또는 명령줄 인수 형식을 사용할 수 있습니다.
-d
xargs
단일 바이트 레코드 구분 기호를 허용하는 옵션은 -0
나중에 GNU xargs(4.2.26에서, 2005년 후반에 출시됨)에 추가된 것과 동일하므로 중복되지만 -d '\0'
, 지금까지 내가 아는 한 여전히 GNU에서 지원됩니다 xargs
.
-0
이들 또는 -d
(및 -r
아래 참조) 이 없으면 xargs
(안정적으로) 사용하는 것이 거의 불가능합니다.
-print0
/는 이후 몇 가지 다른 구현과 심지어 Solaris 11과 같은 일부 상용 SysV 파생 Unix에도 추가되었습니다. 쉘의 내장 명령 -0
도 이를 지원합니다.find
bosh
표준어는 아니지만,-d ''
이는 POSIX 표준의 다음 버전(유틸리티 옵션 과 함께)에서도 가능합니다.read
.
-exec cmd {} + xargs 이상 -0 cmd
-exec cmd {} +
표준이며 이제는 이식성이 뛰어납니다. 비지박스에서는 해당 지원이 여전히 선택 사항이므로 이를 사용할 수 없는 Linux 기반 임베디드 시스템이 있을 수 있습니다. -ok cmd {} +
실행 전에 사용자에게 메시지를 표시하는 변형은 cmd
표준도 아니고 이식성도 없습니다(명령줄이 상당히 커질 수 있으므로 편리하지도 않습니다).
-print0
/ 는 표준은 아니지만 이제 BSD 및 Linux 기반 시스템(GNU, busybox' 및 toybox' 포함)의 / 구현 xargs -0
에서 일반적입니다 . 아직 지원되지 않음find
xargs
AIX에서HP/UX도 마찬가지입니다.
GNU 시스템 외부에서는 NUL로 구분된 레코드를 지원하는 다른 표준 유틸리티( sort
, sed
등 ) 의 다른 구현을 찾는 것이 여전히 드뭅니다 .cut
awk
find
-print0
표준 구현과 함께 사용할 수 있지만 또는 / / ... -z와 동등한 표준 -exec printf '%s\0' {} +
은 없으며 더 일반적으로 NUL은 POSIX 텍스트 유틸리티(또는 일반적인 파일 경로에서도 처리할 수 없습니다. 텍스트 ).xargs -0
sort
sed
grep
일부 BSD를 제외하고 일반적으로 불필요한 파일이 발견되지 않으면 find . -print0 | xargs -0 cmd
인수 없이 한 번만 실행됩니다. cmd
GNU 구현에서는 이를 방지하기 위한 옵션을 xargs
추가 하지만 .-r
-0
에서는 stdin find . -exec cmd {} +
이 cmd
상속되므로 find
예 cmd
를 들어 터미널에서 명령이 실행되면 여전히 사용자와 상호 작용할 수 있습니다.
에서 구현 find . -print0 | xargs -r0 cmd
에 따라 stdin은 /dev/null (GNU와 동일 )이거나 더 나쁘게는 stdin이 나오는 파이프인 ' stdin을 상속 하므로 stdin에서 읽는 경우 심각한 손상을 입힐 것입니다. GNU 구현을 사용하면 대체를 사용 하고 처리하여 이 문제를 해결할 수 있습니다.xargs
cmd
xargs
xargs
find
xargs
-a
xargs -r0a <(find . -print0) cmd # Korn syntax
xargs -r0a <{find . -print0} cmd # rc syntax
xargs -r0a /dev/fd/3 3<(find . -print0) cmd # yash syntax
xargs -r0a (find . -print0|psub) cmd # fish syntax (not parallel though)
하지만 휴대성은 훨씬 떨어집니다.
안정성 측면에서 충돌이 발생하거나 조기에 종료되는 find . -print0 | xargs -0 cmd
경우 (예: 리소스 제한에 도달한 경우) find
심각한 결과를 초래할 수 있습니다 . 이는 find
NUL 구분 기호로 끝나는 것이 보장되지 않는 블록에 출력을 작성하고(예: with find /var/tmp -name '*.tmp'
, 블록은 로 끝날 수 있음 ) /var
구분되지 않은 레코드에 대한 xargs
인수를 계속 제공 하기 때문입니다 . cmd
예를 들어, 우리 예에서는 .로 끝나는 블록을 출력한 후 사망한 경우 as 인수 cmd
(예: rm -rf
)를 사용하여 호출할 수 있습니다./var
find
/var
이 문제는 영향을 받지 않습니다 -exec cmd {} +
.
를 사용하면 find . -exec cmd {} +
종료 상태가 반영 find
되고 cmd
실패하지만 find | xargs
대부분의 셸에서는 종료 상태만 가져오므 xargs
로 모든 파일을 찾을 수 없다는 사실을 놓칠 수 있습니다. 많은 쉘에는 pipefail
이를 완화하는 명령이 있습니다. 궁극적으로 더 많은 유연성을 제공하는 zsh $pipestatus
또는 bash 도 참조하세요 $PIPESTATUS
.
(비표준) -execdir cmd -- {} +
(파일 이름에 접두사를 추가하지 않는 구현에 필요한 참고 --
) 변형을 해결할 수 있습니다.find
./
-exec cmd {} +
xargs
xargs
성능 측면에서 find | xargs
이는 더 많은 작업(적어도 하나의 추가 프로세스 및 파이프를 통해 해당 데이터 푸시)을 의미하며, 이 중 일부는 병렬로 수행되고 find
동시에 xargs
실행되므로 결국 작업이 증가할 수 있습니다. 두 가지가 I/O 액세스를 놓고 경쟁 find
하지 않으므로 cmd
더 많은 전체 리소스를 사용할 수 있기 때문에 경합이 발생합니다. 이러한 병렬성으로 인해 어떤 경우에는 이전 배치에서 CPU를 많이 사용하는 일부 작업을 실행하는 동안(적어도 파이프라인 및 내부 출력이 실행될 때까지) find
더 많은 파일을 계속 검색할 수 있으므로 작업 실행 속도가 더 빨라질 수 있습니다 . ) 버퍼가 가득 찼습니다.)cmd
find
를 사용하면 전체 검색을 중단 find . -exec cmd {} +
하는 것이 더 쉽습니다 . cmd
예를 들어 다음과 같습니다.
find . -exec sh -c 'if some-condition; then kill -s PIPE "$PPID"; exit 1; fi' sh {} +
를 사용하면 find . -print0 | xargs -0 cmd
중단 이 cmd
수행될 수 있지만 파이프 에 다음 블록을 쓰려고 시도할 때까지 종료되지 않습니다.exit 255
xargs
find
-exec cmd {}에 대한 xargs -0 cmd +
이러한 견해의 주된 주장은 그것이 일반적인 의미에서 더 일반적이고, 더 유연하며, 더 다재다능하다는 것입니다.
find
의 출력 -print0
은 에서만 사용할 수 있는 파일 목록의 사후 처리 가능한 표현입니다 xargs -0
. 예를 들어 다음과 같이 할 수 있습니다.
find . -print0 |
grep -z foo |
sort -z
또한 사후 처리, 필터링 및 정렬이 가능한 파일 경로 목록을 얻을 수 있습니다.
파일 경로를 나타내든 다른 것을 나타내든 출력이든 다른 것이든 NUL로 구분된 목록에도 동일하게 xargs -0
사용할 수 있습니다 .find
이와 관련하여 find . -exec cmd {} +
이는 좁은 특수 사용 사례에만 적합합니다(가장 일반적인 사용 사례 중 하나임에도 불구하고).
의 경우 또는 옵션을 사용하여 에 전달되는 인수 수를 제한 xargs
할 수 있습니다 . GNU의 경우 여러 인스턴스를 병렬로 실행하는 옵션 또는 파일 목록 뒤에 추가 인수를 추가할 수 있는 일부 BSD 옵션 도 참조하세요 .-n
-s
cmd
xargs
-P
cmd
xargs -0 -J {} mv {} /dest/
출력을 find . -print0
파일에 저장하고 나중에 처리하여(예: find
완전한 성공 시에만) 다음을 포함하여 xargs -0 cmd < file
출력이 cmd
결과를 방해하는 것을 방지할 수 있습니다 find
(GNU 사용 xargs
):
xargs -r0a =(find . -print0) cmd # zsh
xargs -r0a (find . -print0|psub -f) cmd # fish
특수 처리와 동등한 것은 없습니다 xargs
( 위 내용 참조 ).exit 255
-exec cmd {} +
kill "$PPID"
를 사용하면 find | xargs
다른 로케일이나 더 일반적으로 다른 환경 (변수, 제한, umask 등 포함) 에서 더 쉽게 find
실행할 수 있습니다.xargs cmd
예를 들어, 텍스트가 아닌 파일 이름과 관련된 문제를 해결하기 위해 C 로케일에서 실행해야 하는 경우가 많지만 find
여전히 cmd
사용자의 로케일에서 실행하고 싶은 경우가 있습니다.
LC_ALL=C find . -exec cmd {} +
C 언어 환경에서 실행되며 find
. cmd
그리고
LC_ALL=C find . -exec env -u LC_ALL cmd {} +
첫 번째는 비표준이지만 미리 정의 cmd
하면 LC_ALL
원래 로케일을 복원하는 것이 불가능할 수도 있습니다.
LC_ALL=C find . -print0 | xargs -r0 cmd
find
로케일을 C로 변경하면 됩니다.
특별한 경우:
find . -exec sudo cmd {} +
sudo
일반적으로 환경 변수 설정으로 인해 SUDO_COMMAND
인수 목록이 복제되므로 args+env 크기 제한을 피할 수 있는 방법이 없습니다 .
find . -print0 | sudo xargs -r0 cmd
$SUDO_COMMAND
이 경우에는 include만 xargs -0 cmd
사용 하므로 문제가 없습니다 find . -print0 | xargs -r0 sudo cmd
.
또한보십시오:
sudo find . -print0 | xargs -r0 cmd
파일 목록은 에 의해 발견되지만 root
원래 cmd
사용자로 실행됩니다. 또는
find . -print0 | (USERNAME=some-user; xargs -r0 cmd)
zsh와 같은 셸에서는 (e)uid, (e)gid 변경을 기본적으로 지원합니다.
find . -print0 | xargs -r0 -- "${cmd[@]}"
$cmd
배열에 포함된 내용에 관계없이 작동하지만
find . -exec "${cmd[@]}" {} +
배열의 요소 중 하나라도 연속된 요소가 $cmd
있거나 ;
포함되어 있으면 실패합니다.{}
+