xargs ("eXtended ARGuments"에 대한 줄임말)는 표준 입력에서 명령을 빌드하고 실행하기 위해 사용되는 유닉스와 대부분의 유닉스-계열 운영 시스템의 명령입니다. 그것은 표준 입력으로부터 입력을 명령에 대한 인수로 변환합니다.
grep 및 awk와 같은 일부 명령은 명령줄 인수 또는 표준 입력에서 입력을 받을 수 있습니다. 어쨌든, cp 및 echo와 같은 다른 것은 입력을 인수로만 사용할 수 있으며, 이것이 xargs가 필요한 이유입니다.
xargs 명령은 역시 IBM i 운영 시스템에 이식되었습니다.
Examples
xargs 명령의 한 가지 사용 경우는 rm 명령을 사용하여 파일 목록을 제거하는 것입니다. POSIX 시스템은 명령줄의 최대 전체 길이에 대한 ARG_MAX를 가지므로, 그 명령은 "인수 목록이 너무 깁니다"라는 오류 메시지와 함께 실패할 수 있습니다 (명령줄의 길이에 exec 시스템 호출의 한계가 초과되었음을 의미합니다): rm /path/* 또는 rm $(find /path -type f). (후자의 호출은 올바르지 않은데, 왜냐하면 그것은 출력에서 globs를 확장할 수 있기 때문입니다.)
이것은 인수 목록을 수용할 수 있을 만큼 작은 하위목록으로 나누기 위해 xargs 명령을 사용하여 다시 작성할 수 있습니다:
find /path -type f -print | xargs rm
위의 예제에서, find utility 유틸리티는 xargs의 입력에 긴 파일 이름 목록을 제공합니다. xargs는 그런-다음 이 목록을 하위목록으로 분할하고 모든 각 하위목록에 대해 한 번씩 rm을 호출합니다.
xargs는 역시 -P maxprocs 인수로 얼마나 많은 병렬 프로세서가 입력 인수 목록에 걸쳐 명령을 수행하기 위해 사용되어야 하는지 지정하기 위해 연산을 병렬화하기 위해 사용될 수 있습니다. 어쨌든, 출력 스트림이 동기화되지 않을 수 있습니다. 이것은 가능한 경우에 --output file 인수를 사용하고, 그런-다음 처리 후 결과를 조합함으로써 극복될 수 있습니다. 다음 예제는 24개의 프로세스를 큐에 넣고 또 다른 프로세스를 시작하기 전에 각각이 완료될 때까지 기다립니다.
find /path -name '*.foo' | xargs -P 24 -I '{}' /cpu/bound/process '{}' -o '{}'.out
xargs는 종종 역따옴표 표기법 (`...` 또는 $(...))에 의해 표시되는 많은 쉘의 명령 대체 기능과 같은 기능을 다룹니다. xargs는 역시 find, locate 및 grep과 같은 긴 파일 목록을 출력하는 명령에도 좋은 동반자이지만, 만약 -0없이 xargs는 ', " 및 스페이스를 포함하는 파일 이름과 함께 나쁘게 다루어지기 때문에, 오직 만약 -0 (또는 동등하게 --null)를 사용하면 좋은 동반자입니다. GNU Parallel은 파일 이름이 ', ", 및 스페이스를 포함할 수 있을 때, find, locate 및 grep과 더 나은 호환성을 제공하는 유사한 도구입니다 (줄 바꿈에는 여전히 -0이 필요함).
Placement of arguments
-I option: single argument
xargs 명령은 명령줄 끝이 아닌 어떤 위치에 나열된 인수를 삽입하기 위한 옵션을 제공합니다. xargs에 대한 -I 옵션은 명령이 실행되기 전에 제공된 입력으로 대체될 문자열을 취합니다. 공통적인 선택은 %입니다.
$ mkdir ~/backups
$ find /path -type f -name '*~' -print0 | xargs -0 -I % cp -a % ~/backups
대체할 문자열은 명령 부분에 여러 번 나타날 수 있습니다. -I를 사용하면 매번 사용되는 줄 수가 하나로 제한됩니다.
Shell trick: any number
유사한 효과를 얻는 또 다른 방법은 쉘을 실행된 명령으로 사용하고, 해당 쉘에서 복잡성을 처리하는 것입니다. 예를 들어:
$ mkdir ~/backups
$ find /path -type f -name '*~' -print0 | xargs -0 sh -c 'for filename; do cp -a "$filename" ~/backups; done' sh
줄 끝에 있는 단어 sh는 POSIX 쉘 sh -c에 대해 $0, 위치 매개변수 (argv)의 "실행-가능한 이름" 부분을 채우기 위한 것입니다. 만약 그것이 존재하지 않으면, 첫 번째 일치된 파일의 이름이 대신 $0에 할당되고 파일은 ~/backups에 복사되지 않습니다. 예를 들어 my-xargs-script와 같은 해당 공백을 채우기 위해 임의의 다른 단어를 사용할 수도 있습니다:
cp는 한 번에 여러 파일을 허용하므로, 다음을 간단히 수행할 수도 있습니다:
$ find /path -type f -name '*~' -print0 | xargs -0 sh -c 'if [ $# -gt 0 ]; then cp -a "$@" ~/backup; fi' sh
이 스크립트는 임의의 전달된 인수가 있을 때 제공된 모든 파일과 함께 cp를 실행합니다. 이렇게 하면 sh를 각 호출에 대해 cp를 한 번만 호출하므로 더 효율적입니다.
Separator problem
많은 유닉스 유틸리티는 줄-지향적입니다. 이것들은 행이 ', ", 또는 스페이스를 포함하지 않는 한 xargs와 함께 작동할 수 있습니다. 일부 유닉스 유틸리티는 NUL을 레코드 구분 기호로 사용할 수 있습니다 (예를 들어 Perl (\n 대신에 -0와 \0을 요구함), locate (-0 사용을 요구함), find (-print0 사용을 요구함), grep (-z 또는 -Z 요구함), sort (-z 사용을 요구함)). xargs에 대해 -0을 사용하면 문제를 다룰 수 있지만, 많은 유닉스 유틸리티는 NUL을 구분자로 사용할 수 없습니다 (예를 들어, head, tail, ls, echo, sed, tar -v, wc, which).
그러나 종종 사람들은 이것을 잊고 xargs도 줄-지향적이라고 가정하며, 이것은 사실이 아닙니다 (기본 xargs에 따라 줄 바꿈과 줄 내에 공백이 구분되며, 공백을 갖는 부분문자열은 작은따옴표 또는 큰따옴표로 묶어야 합니다).
구분자는 다음과 같습니다:
# Make some targets to practice on
touch important_file
touch 'not important_file'
mkdir -p '12" records'
find . -name not\* | tail -1 | xargs rm
find \! -name . -type d | tail -1 | xargs rmdir
위를 실행하면 important_file이 제거될 것이지만 12" records라는 디렉토리도 제거하지 않고, not important_file이라는 파일도 제거하지 않을 것입니다.
적절한 수정은 GNU-지정 -print0 옵션을 사용하는 것이지만, tail (및 기타 도구)는 NUL-종료 문자열을 지원하지 않습니다:
# use the same preparation commands as above
find . -name not\* -print0 | xargs -0 rm
find \! -name . -type d -print0 | xargs -0 rmdir
-print0 옵션을 사용할 때, 항목은 end-of-line이 아닌 null 문자로 구분됩니다. 이것은 더 자세한 다음 명령과 동등합니다: find . -name not\* | tr \\n \\0 | xargs -0 rm 또는 더 짧게, -d (delimiter) 옵션을 갖는 xargs를 (비-POSIZ) 줄-지향적 모드로 전환함으로써: find . -name not\* | xargs -d '\n' rm
그러나 일반적으로 -print0와 -0를 사용하는 것이 선호되어야 하는데, 왜냐하면 파일이름에서 줄 바꿈이 여전히 하나의 문제이기 때문입니다.
GNU parallel은 같은 옵션을 가지도록 설계된 xargs의 대안이지만, 줄-지향적입니다. 따라서, 대신 GNU Parallel을 사용하면, 위의 내용이 예상대로 작동합니다.
xargs가 -0도 지원하지 않고 -d 옵션 (예를 들어, Solaris, AIX)을 지원하지 않는 유닉스 환경에 대해, POSIX 표준은 모든 각 문자를 간단히 백슬래시-탈출할 수 있다고 명시합니다: find . -name not\* | sed 's/\(.\)/\\\1/g' | xargs rm. 대안적으로, GNU parallel을 사용하거나 find의 -exec ... + 기능을 사용하여, xargs 사용을 전혀 피할 수 있습니다.
Operating on a subset of arguments at a time
오직 한 번에 하나 또는 두 개의 인수를 수락할 수 있는 명령을 처리할 수 있습니다. 예를 들어, diff 명령은 한 번에 두 개의 파일에서 연산합니다. xargs에 대한 -n 옵션은 주어진 명령에 제공할 한 번에 몇 개의 인수를 지정합니다. 그 명령은 모든 입력이 소진될 때까지 반복적으로 호출될 것입니다. 마지막 호출에서 불충분한 입력이 있으면 원하는 인수의 숫자보다 더 적게 얻을 수 있습니다. xargs를 입력을 한 줄에 두 개의 인수로 나누기 위해 사용하십시오:
$ echo {0..9} | xargs -n 2
0 1
2 3
4 5
6 7
8 9
한 번에 지정된 숫자의 인수를 기반으로 실행하는 것 외에도, -L 1 옵션을 갖는 각 입력 줄에 대해 명령을 호출할 수 있습니다. 한 번에 임의의 숫자의 줄을 사용할 수 있지만, 하나가 가장 공통적입니다. 다음은 모든 각 git 커밋을 부모와 비교하는 diff하는 방법입니다.
$ git log --format="%H %P" | xargs -L 1 git diff
Encoding problem
xargs의 인수 구분자 처리는 기본 모드에서 xargs 프로그램을 사용할 때의 유일한 문제가 아닙니다. 파일이름을 조작하기 위해 자주 사용되는 대부분의 유닉스 도구 (예를 들어, sed, basename, sort, 등)는 텍스트 처리 도구입니다. 어쨌든, 유닉스 경로 이름은 실제로 텍스트가 아닙니다. 경로 이름 /aaa/bbb/ccc를 생각해 보십시오. /aaa 디렉토리와 그 bbb 하위디렉토리는 일반적으로 다른 환경을 갖는 다른 사용자에 의해 생성될 수 있습니다. 이것은 이들 사용자가 다른 로케일 설정을 가질 수 있음을 의미하고, 이것은 aaa 및 bbb가 반드시 같은 문자 인코딩을 가질 필요조차 없음을 의미합니다. 예를 들어, aaa는 UTF-8이고 bbb는 EUC-KR일 수 있습니다. 결과적으로, 유닉스 시스템에서 절대 경로 이름은 단일 문자 인코딩 아래에서 텍스트로 올바르게 처리되지 않을 수 있습니다. 텍스트인 입력에 의존하는 도구는 그러한 문자열에서 실패할 수 있습니다.
이 문제에 대해 한 가지 해결 방법은 필연적으로 입력의 바이트를 있는 그대로 처리하는 C 로케일에서 그러한 도구를 실행하는 것입니다. 어쨌든, 이것은 사용자가 예상하지 못한 방법으로 도구의 동작을 변경할 것입니다 (예를 들어, 대소문자 접기 동작에 대한 사용자의 일부 기대가 충족되지 않을 수 있습니다).
External links
- xargs: construct argument lists and invoke utility – Commands & Utilities Reference, The Single UNIX Specification, Issue 7 from The Open Group
- Linux Xargs Command Tutorial With Examples
Manual pages
- xargs(1) – GNU Findutils reference
- xargs(1): construct argument list(s) and execute utility – FreeBSD General Commands Manual
- xargs(1): construct argument list(s) and execute utility – NetBSD General Commands Manual
- xargs(1): construct argument list(s) and execute utility – OpenBSD General Commands Manual
- xargs(1): construct argument lists and invoke utility – Solaris 10 User Commands Reference Manual