xargs가 각 입력 줄에 대해 명령을 한 번 실행하도록하십시오.


341

xargs가 주어진 각 입력 행에 대해 정확히 한 번만 명령을 실행하도록하려면 어떻게해야합니까? 기본적으로 행을 청크하고 명령을 한 번 실행하여 각 인스턴스에 여러 행을 전달하는 것이 기본 동작입니다.

에서 http://en.wikipedia.org/wiki/Xargs :

찾기 / 경로-타입 f -print0 | xargs -0 rm

이 예제에서 find는 긴 파일 이름 목록으로 xargs의 입력을 공급합니다. 그런 다음 xargs는이 목록을 하위 목록으로 나누고 모든 하위 목록에 대해 rm을 한 번 호출합니다. 이것은 기능적으로 동등한 버전보다 효율적입니다.

/ path -type f -exec rm '{}'찾기 \;

find에 "exec"플래그가 있음을 알고 있습니다. 다른 리소스의 예제를 인용하고 있습니다.


4
당신이 제공하는 예에서, find /path -type f -delete훨씬 더 효율적일 것입니다 :)
tzot

xargs를 사용하지 마십시오 ...
Naib

6
OP, 나는이 질문이 매우 오래되었다는 것을 알고 있지만 여전히 Google에 나타나고 IMHO가 받아 들인 대답이 잘못되었습니다. 아래의 더 긴 답변을 참조하십시오.
Tobia

@Tobia의 답변으로 수락을 전환하는 것이 훨씬 좋습니다. 허용되는 답변은 이름의 공백을 처리하지 않으며 xargs의 주요 기능 중 하나 인 xargs 명령에 여러 인수를 허용하지 않습니다.
그레이

답변:


394

입력에 공백이없는 경우에만 다음이 작동합니다.

xargs -L 1
xargs --max-lines=1 # synonym for the -L option

매뉴얼 페이지에서 :

-L max-lines
          Use at most max-lines nonblank input lines per command line.
          Trailing blanks cause an input line to be logically continued  on
          the next input line.  Implies -x.

13
나를 위해 xargs -n 1당신이 준 것이 "인수 목록이 너무 깁니다"는 것처럼 나올 수 있습니다 .
Wernight

19
경우 MAX-LINES1로 그것을 기본값을 생략, 따라서 xargs -l충분하다. 참조하십시오 info xargs.
Thor

3
@Wernight : "-n1"은 입력 라인 당 1 개의 호출을 제공하지 않습니다. 입력 라인이 너무 길었을 수 있습니다. 데모 : echo "foo bar" | xargs -n1 echo. 따라서 'ls'와 같은 것을 파이프하면 공간을 잘 처리하지 못합니다.
gatoatigrado

8
이것은 잘못이다. -L 1원래의 질문에 대답하지 않고 -n 1가능한 해석 중 하나에서만 대답합니다 . 아래의 긴 답변을 참조하십시오.
Tobia

2
@ Tobia : 입력 줄에 관한 원래 질문에 대답합니다. 정확히 무엇입니까 -L 1. 나에게 OP는 기본 청크 동작을 피하려고 노력하는 것처럼 보였으며, 이것이 받아 들여 졌기 때문에 내가 옳다고 생각합니다. 귀하의 답변은 청킹 동작을 원하는 약간 다른 사용 사례를 다루고 있습니다.
Draemon

207

이 페이지의 모든 기존 답변이 올바른 것으로 표시된 답변을 포함하여 잘못된 것 같습니다. 그것은 질문이 모호하게 표현되어 있다는 사실에서 비롯됩니다.

요약 :   명령을 "한 줄씩 입력 할 때마다 정확히 한 번" 명령을 실행하려면 전체 행 (줄 바꿈없이)을 단일 인수 로 명령에 전달 하면 가장 적합한 UNIX 호환 방법입니다.

... | tr '\n' '\0' | xargs -0 -n1 ...

GNU xargs는 유용한 확장 기능을 가지고 있거나 가지고 있지 않을 수도 tr있지만 OS X 및 기타 UNIX 시스템에서는 사용할 수 없습니다.

이제 긴 설명을 위해…


xargs를 사용할 때 고려해야 할 두 가지 문제가 있습니다.

  1. 입력을 "인수"로 어떻게 분할합니까? 과
  2. 한 번에 자식 명령을 전달할 인수 수

xargs의 동작을 테스트하려면 실행 횟수와 인수 수를 보여주는 유틸리티가 필요합니다. 표준 유틸리티가 있는지 모르겠지만 bash에서 쉽게 코딩 할 수 있습니다.

#!/bin/bash
echo -n "-> "; for a in "$@"; do echo -n "\"$a\" "; done; echo

show현재 디렉토리에 파일 을 저장하고 실행 가능 하다고 가정하면 다음과 같이 작동합니다.

$ ./show one two 'three and four'
-> "one" "two" "three and four" 

이제 원래의 질문이 실제로 포인트 2에 관한 것이라면 (제 생각에 몇 번 이상 읽은 후) 다음과 같이 읽습니다 (굵게 변경).

주어진 입력 인수 마다 xargs가 명령을 정확히 한 번만 실행하도록하려면 어떻게 해야합니까? 기본 동작은 입력을 인수 로 청크하고 가능한 한 적은 수의 명령 실행하여 각 인스턴스에 여러 인수 를 전달하는 것 입니다.

대답은 -n 1입니다.

xargs의 기본 동작을 비교하여 입력을 공백으로 나누고 명령을 가능한 한 적게 호출합니다.

$ echo one two 'three and four' | xargs ./show 
-> "one" "two" "three" "and" "four" 

그리고 그것의 행동 -n 1:

$ echo one two 'three and four' | xargs -n 1 ./show 
-> "one" 
-> "two" 
-> "three" 
-> "and" 
-> "four" 

반면에 원래의 질문이 포인트 1에 관한 것이었고 입력 분할과 같이 읽히는 경우 (여기에 오는 많은 사람들이 그런 경우라고 생각하거나 두 가지 문제를 혼란스럽게합니다) :

xargs가 주어진 각 입력 행에 대해 정확히 하나의 인수 로 명령 실행하도록하려면 어떻게 해야합니까? 기본 동작은 공백 주위 의 행을 청크하는 것 입니다.

대답은 더 미묘합니다.

-L 1도움이 될 수 있다고 생각 하지만 인수 구문 분석을 변경하지는 않습니다. 각 입력 행에 대해 한 번만 명령을 실행하며 해당 입력 행에있는 수만큼의 인수를 사용합니다.

$ echo $'one\ntwo\nthree and four' | xargs -L 1 ./show 
-> "one" 
-> "two" 
-> "three" "and" "four" 

뿐만 아니라 줄이 공백으로 끝나면 다음 줄에 추가됩니다.

$ echo $'one \ntwo\nthree and four' | xargs -L 1 ./show 
-> "one" "two" 
-> "three" "and" "four" 

분명히, -L에 대한 방법 xargs를 변경하지 않는 것은 인수로 입력을 분할합니다.

크로스 플랫폼 방식 (GNU 확장명 제외)으로 그렇게하는 유일한 인수 -0는 입력을 NUL 바이트로 분할하는 것입니다.

그런 다음 도움을 받아 줄 바꿈을 NUL로 변환하는 것입니다 tr.

$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show 
-> "one " "two" "three and four" 

이제 인수 구문 분석은 후행 공백을 포함하여 올바르게 보입니다.

마지막 으로이 기술을와 결합하면 -n 1입력 줄마다 정확히 하나의 명령 실행이 발생합니다. 입력에 관계없이 원래 질문을 볼 수있는 또 다른 방법 일 수 있습니다 (제목이 주어지면 가장 직관적 일 수 있습니다).

$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 -n1 ./show 
-> "one " 
-> "two" 
-> "three and four" 

이것이 더 나은 답변 인 것 같습니다. 그러나, 나는 여전히 -L과 -n의 차이점이 무엇인지 이해하지 못합니다. 조금 더 설명해 주시겠습니까?
olala

5
@olala -L는 입력 행마다 한 번씩 명령을 실행합니다 (그러나 행 끝에있는 공백이 다음 행에 연결되고 행은 공백에 따라 인수로 분할됩니다). -n입력 인수마다 한 번씩 명령 을 실행합니다. 당신의 수를 계산하면 ->출력 예제를, 그 스크립트가 횟수입니다 ./show실행됩니다.
Tobia

내가 참조! 줄 끝의 공백이 다음 줄과 결합한다는 것을 알지 못했습니다. 감사!
olala

4
GNU xargs는 여러분이 제거 할 수있는 유용한 확장 기능을 가지고있을 수도 있고 그렇지 않을 수도 있습니다tr . from xargs --help-d, --delimiter = CHARACTER 입력 스트림의 항목은 공백이 아닌 CHARACTER로 구분됩니다. 견적 및 백 슬래시 처리 및 논리적 EOF 처리 비활성화
Piotr Dobrogost

이 답변은에 대해 혼란스러워 보입니다 -L. -L한 줄에 스크립트를 몇 번이나 실행해야하는지, 한 번에 몇 줄의 입력 데이터를 소비해야하는지 말합니다.
Moberg

22

에서 나오는 모든 줄 (예 : 결과)에 대해 명령을 실행하려면 find무엇을 xargs위해 필요한가?

시험:

find 당신의 명령을 경로-type f -exec {} \;

여기서 리터럴 {}은 파일 이름으로 대체되고 리터럴 \;find사용자 정의 명령이 종료됨을 알기 위해 필요합니다 .

편집하다:

(질문을 편집 한 후에 대해 알고 있음을 -exec나타냄)

보낸 사람 man xargs:

-L max-lines 명령 행당 최대 max-lines 비 공백 입력 라인을
사용하십시오 . 후행 공백은 입력 라인이 다음 입력 라인에서 논리적으로 계속되도록합니다. -x를 의미합니다.

공백으로 끝나는 파일 이름은 다음을 사용하면 문제를 일으킬 수 있습니다 xargs.

$ mkdir /tmp/bax; cd /tmp/bax
$ touch a\  b c\  c
$ find . -type f -print | xargs -L1 wc -l
0 ./c
0 ./c
0 total
0 ./b
wc: ./a: No such file or directory

당신이 걱정하지 않도록 경우 -exec옵션, 당신은 더 나은 사용 -print0-0:

$ find . -type f -print0 | xargs -0L1 wc -l
0 ./c
0 ./c
0 ./b
0 ./a

18

xargs가 주어진 각 입력 행에 대해 정확히 한 번만 명령을 실행하도록하려면 어떻게해야합니까?

-L 1간단한 해결책이지만 파일 중 하나에 공백이 있으면 작동하지 않습니다. 이것은 -print0공백 대신 '\ 0'문자로 인수를 구분하는 find 인수 의 핵심 기능입니다 . 예를 들면 다음과 같습니다.

echo "file with space.txt" | xargs -L 1 ls
ls: file: No such file or directory
ls: with: No such file or directory
ls: space.txt: No such file or directory

더 나은 해결책은 tr개행을 null ( \0) 문자 로 변환 한 다음 xargs -0인수 를 사용하는 것 입니다. 예를 들면 다음과 같습니다.

echo "file with space.txt" | tr '\n' '\0' | xargs -0 ls
file with space.txt

그런 다음 호출 수를 제한해야하는 경우 -n 1인수를 사용하여 각 입력에 대해 프로그램을 한 번 호출 할 수 있습니다 .

echo "file with space.txt" | tr '\n' '\0' | xargs -0 -n 1 ls

또한 나누기를 널로 변환 하기 전에 find의 출력을 필터링 할 수 있습니다 .

find . -name \*.xml | grep -v /target/ | tr '\n' '\0' | xargs -0 tar -cf xml.tar

1
두 번째 코드 블록 tr '\ n' '\ 0 \ => tr'\ n ''\ 0 '에 구문 오류가 있습니다.이 문제를 해결하려고했지만 "편집은 6 자 이상이어야합니다." 자식 내 변화 미만 6 개 문자 때문에) 커밋 거부로 바보
htaccess로

1
"사용하는 또 다른 문제 -L는 각 xargs명령 호출 에 대해 여러 개의 인수를 허용하지 않는다는 것 입니다."?
Moberg

관련 정보 @Moberg를 제거하기 위해 답변을 개선했습니다.
그레이

11

다른 대안 ...

find /path -type f | while read ln; do echo "processing $ln"; done

9

이 두 가지 방법도 작동하며 find를 사용하지 않는 다른 명령에서도 작동합니다!

xargs -I '{}' rm '{}'
xargs -i rm '{}'

사용 사례 예 :

find . -name "*.pyc" | xargs -i rm '{}'

pyc 파일에 공백이 있어도이 디렉토리 아래의 모든 pyc 파일이 삭제됩니다.


이것은 최적이 아닌 모든 요소에 대해 하나의 유틸리티 호출을 발행합니다.
그레이

7
find path -type f | xargs -L1 command 

당신이 필요한 전부입니다.


4

다음 명령은 모든 파일 (-유형 f)을 /path찾은 다음 cp현재 폴더로 사용하여 복사 합니다. 명령 행 -I %에서 플레이스 홀더 문자를 지정하여 if 를 사용하면 cp파일 이름 뒤에 인수를 배치 할 수 있습니다.

find /path -type f -print0 | xargs -0 -I % cp % .

xargs (GNU findutils) 4.4.0으로 테스트


2

--max-lines 또는 --max-args 플래그를 각각 사용하여 행 수 또는 인수 (각 인수 사이에 공백이있는 경우)를 제한 할 수 있습니다.

  -L max-lines
         Use at most max-lines nonblank input lines per command line.  Trailing blanks cause an input line to be logically continued on the next  input
         line.  Implies -x.

  --max-lines[=max-lines], -l[max-lines]
         Synonym  for  the -L option.  Unlike -L, the max-lines argument is optional.  If max-args is not specified, it defaults to one.  The -l option
         is deprecated since the POSIX standard specifies -L instead.

  --max-args=max-args, -n max-args
         Use at most max-args arguments per command line.  Fewer than max-args arguments will be used if the size (see  the  -s  option)  is  exceeded,
         unless the -x option is given, in which case xargs will exit.

0

위의 Tobia의 답변에 의견을 추가 할만 큼 평판이 좋지 않은 것 같습니다 . 그래서이 "답변"을 추가 xargs하여 Windows 플랫폼에서 동일한 방식으로 실험하려는 사람들을 도울 것 입니다.

다음은 Tobia의 신속하게 코딩 된 "show"스크립트와 동일한 작업을 수행하는 Windows 배치 파일입니다.

@echo off
REM
REM  cool trick of using "set" to echo without new line
REM  (from:  http://www.psteiner.com/2012/05/windows-batch-echo-without-new-line.html)
REM
if "%~1" == "" (
    exit /b
)

<nul set /p=Args:  "%~1"
shift

:start
if not "%~1" == "" (
    <nul set /p=, "%~1"
    shift
    goto start
)
echo.

0

@Draemon 답변은 파일에 공간이 있어도 "-0"으로 올바른 것 같습니다.

xargs 명령을 시도했는데 "-0"이 "-L"과 완벽하게 작동한다는 것을 알았습니다. 공백조차도 처리됩니다 (입력이 null 종료 된 경우). 다음은 예입니다.

#touch "file with space"
#touch "file1"
#touch "file2"

다음은 널을 분할하고 목록의 각 인수에 대해 명령을 실행합니다.

 #find . -name 'file*' -print0 | xargs -0 -L1
./file with space
./file1
./file2

그래서 -L1사용하는 경우 각 널 종료 문자에 인수를 실행합니다 "-0". 차이점을 보려면 다음을 시도하십시오.

 #find . -name 'file*' -print0 | xargs -0 | xargs -L1
 ./file with space ./file1 ./file2

이조 차도 한 번 실행됩니다.

 #find . -name 'file*' -print0  | xargs -0  | xargs -0 -L1
./file with space ./file1 ./file2

"-L"이 널 바이트로 분할되지 않으므로 명령이 한 번 실행됩니다. 작동하려면 "-0"과 "-L"을 모두 제공해야합니다.


-3

예를 들어, find의 출력을 xargs로 파이핑하는 것은 find의 -exec 옵션의 표준 동작이 각 발견 된 파일에 대해 명령을 한 번 실행하는 것입니다. find를 사용하고 표준 동작을 원한다면 대답은 간단합니다. 우선 xargs를 사용하지 마십시오.


실제로 OP의 편집에서 암시 할 수 있는 것은 입력 데이터와 관련 find이 없으므로 -exec옵션을 선호하지 않는 것입니다.
tzot 2009

-3

현재 또는 하위 폴더의 모든 build.xml에서 ant task clean-all을 실행하십시오.

find . -name 'build.xml' -exec ant -f {} clean-all \;

모든 사람이 ant설치 한 것은 아닙니다 .
그레이
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.