찾다. - find로 0 | xargs -0 cmd를 인쇄합니다. -exec 명령 {} +

찾다. - find로 0 | xargs -0 cmd를 인쇄합니다. -exec 명령 {} +
  • 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이를 안정적으로 수행할 수는 없습니다.

-01990년에 GNU에 옵션이 추가되면서 xargs새로운 -print0옵션이 추가되었습니다. findGNU 작성자가 옵션을 추가했을 때 SysV에 대해 몰랐던 것이 확실한 것입니다 find. 그 이후로 NUL로 구분된 문제를 처리하기 위해 -exec {} +일부 -0/// --null옵션이 다른 GNU 유틸리티에 점차 추가되었습니다.-z--zero교환모든 파일 경로와 더 일반적으로 모든 C 문자열 또는 명령줄 인수 형식을 사용할 수 있습니다.

-dxargs단일 바이트 레코드 구분 기호를 허용하는 옵션은 -0나중에 GNU xargs(4.2.26에서, 2005년 후반에 출시됨)에 추가된 것과 동일하므로 중복되지만 -d '\0', 지금까지 내가 아는 한 여전히 GNU에서 지원됩니다 xargs.

-0이들 또는 -d(및 -r아래 참조) 이 없으면 xargs(안정적으로) 사용하는 것이 거의 불가능합니다.

-print0/는 이후 몇 가지 다른 구현과 심지어 Solaris 11과 같은 일부 상용 SysV 파생 Unix에도 추가되었습니다. 쉘의 내장 명령 -0도 이를 지원합니다.findbosh

표준어는 아니지만,-d ''이는 POSIX 표준의 다음 버전(유틸리티 옵션 과 함께)에서도 가능합니다.read.

-exec cmd {} + xargs 이상 -0 cmd

-exec cmd {} +표준이며 이제는 이식성이 뛰어납니다. 비지박스에서는 해당 지원이 여전히 선택 사항이므로 이를 사용할 수 없는 Linux 기반 임베디드 시스템이 있을 수 있습니다. -ok cmd {} +실행 전에 사용자에게 메시지를 표시하는 변형은 cmd표준도 아니고 이식성도 없습니다(명령줄이 상당히 커질 수 있으므로 편리하지도 않습니다).

-print0/ 는 표준은 아니지만 이제 BSD 및 Linux 기반 시스템(GNU, busybox' 및 toybox' 포함)의 / 구현 xargs -0에서 일반적입니다 . 아직 지원되지 않음findxargsAIX에서HP/UX도 마찬가지입니다.

GNU 시스템 외부에서는 NUL로 구분된 레코드를 지원하는 다른 표준 유틸리티( sort, sed등 ) 의 다른 구현을 찾는 것이 여전히 드뭅니다 .cutawk

find-print0표준 구현과 함께 사용할 수 있지만 또는 / / ... -z와 동등한 표준 -exec printf '%s\0' {} +은 없으며 더 일반적으로 NUL은 POSIX 텍스트 유틸리티(또는 일반적인 파일 경로에서도 처리할 수 없습니다. 텍스트 ).xargs -0sortsedgrep

일부 BSD를 제외하고 일반적으로 불필요한 파일이 발견되지 않으면 find . -print0 | xargs -0 cmd인수 없이 한 번만 실행됩니다. cmdGNU 구현에서는 이를 방지하기 위한 옵션을 xargs추가 하지만 .-r-0

에서는 stdin find . -exec cmd {} +cmd상속되므로 findcmd를 들어 터미널에서 명령이 실행되면 여전히 사용자와 상호 작용할 수 있습니다.

에서 구현 find . -print0 | xargs -r0 cmd에 따라 stdin은 /dev/null (GNU와 동일 )이거나 더 나쁘게는 stdin이 나오는 파이프인 ' stdin을 상속 하므로 stdin에서 읽는 경우 심각한 손상을 입힐 것입니다. GNU 구현을 사용하면 대체를 사용 하고 처리하여 이 문제를 해결할 수 있습니다.xargscmdxargsxargsfindxargs-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심각한 결과를 초래할 수 있습니다 . 이는 findNUL 구분 기호로 끝나는 것이 보장되지 않는 블록에 출력을 작성하고(예: with find /var/tmp -name '*.tmp', 블록은 로 끝날 수 있음 ) /var구분되지 않은 레코드에 대한 xargs인수를 계속 제공 하기 때문입니다 . cmd예를 들어, 우리 예에서는 .로 끝나는 블록을 출력한 후 사망한 경우 as 인수 cmd(예: rm -rf)를 사용하여 호출할 수 있습니다./varfind/var

이 문제는 영향을 받지 않습니다 -exec cmd {} +.

를 사용하면 find . -exec cmd {} +종료 상태가 반영 find되고 cmd실패하지만 find | xargs대부분의 셸에서는 종료 상태만 가져오므 xargs로 모든 파일을 찾을 수 없다는 사실을 놓칠 수 있습니다. 많은 쉘에는 pipefail이를 완화하는 명령이 있습니다. 궁극적으로 더 많은 유연성을 제공하는 zsh $pipestatus또는 bash 도 참조하세요 $PIPESTATUS.

(비표준) -execdir cmd -- {} +(파일 이름에 접두사를 추가하지 않는 구현에 필요한 참고 --) 변형을 해결할 수 있습니다.find./-exec cmd {} +xargsxargs

성능 측면에서 find | xargs이는 더 많은 작업(적어도 하나의 추가 프로세스 및 파이프를 통해 해당 데이터 푸시)을 의미하며, 이 중 일부는 병렬로 수행되고 find동시에 xargs실행되므로 결국 작업이 증가할 수 있습니다. 두 가지가 I/O 액세스를 놓고 경쟁 find하지 않으므로 cmd더 많은 전체 리소스를 사용할 수 있기 때문에 경합이 발생합니다. 이러한 병렬성으로 인해 어떤 경우에는 이전 배치에서 CPU를 많이 사용하는 일부 작업을 실행하는 동안(적어도 파이프라인 및 내부 출력이 실행될 때까지) find더 많은 파일을 계속 검색할 수 있으므로 작업 실행 속도가 더 빨라질 수 있습니다 . ) 버퍼가 가득 찼습니다.)cmdfind

를 사용하면 전체 검색을 중단 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 255xargsfind

-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-scmdxargs-Pcmdxargs -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있거나 ;포함되어 있으면 실패합니다.{}+

관련 정보