xargs로 여러 명령 실행


310
cat a.txt | xargs -I % echo %

위의 예에서 xargs는 echo %명령 인수로 사용됩니다. 그러나 어떤 경우에는 인수 대신 하나의 인수를 처리하기 위해 여러 명령이 필요합니다. 예를 들면 다음과 같습니다.

cat a.txt | xargs -I % {command1; command2; ... }

그러나 xargs는이 양식을 허용하지 않습니다. 내가 아는 한 가지 해결책은 명령을 래핑하는 함수를 정의 할 수 있지만 파이프 라인이 아니며 선호하지 않습니다. 다른 해결책이 있습니까?


1
이러한 답변의 대부분은 보안 취약점 입니다. 잠재적으로 좋은 대답은 여기를 참조하십시오.
Mateen Ulhaq

2
나는 거의 모든 것에 xargs를 사용하지만 문자열 안에 명령을 넣고 명시 적으로 서브 쉘을 만드는 것을 싫어한다. while여러 명령을 포함 할 수 있는 루프 로 파이프하는 방법을 배우기 직전입니다 .
Sridhar Sarnobat

답변:


443
cat a.txt | xargs -d $'\n' sh -c 'for arg do command1 "$arg"; command2 "$arg"; ...; done' _

... 또는,없는 고양이의 쓸모를 사용 :

<a.txt xargs -d $'\n' sh -c 'for arg do command1 "$arg"; command2 "$arg"; ...; done' _

더 좋은 점을 설명하려면 :

  • 를 사용하는 "$arg"대신에 %(그리고이없는 -I에서 xargs명령 줄) 보안상의 이유로입니다 : 데이터 전달 sh의 명령 줄 인수 목록 대신 데이터 같은 (포함 할 수 있다는 코드 방지의 콘텐츠로 대체 $(rm -rf ~), 특히 걸릴 악성 예) 코드로 실행되지 않습니다.

  • 마찬가지로, 사용 -d $'\n'하는 GNU 확장 xargs은 입력 파일의 각 줄을 별도의 데이터 항목으로 취급합니다. -0xargs가 읽은 스트림에 쉘과 유사한 (그러나 쉘과 호환 되지는 않지만 ) 구문 분석을 적용하지 못하게하려면 이 또는 개행 문자 대신 NUL이 필요합니다 . (GNU xargs가없는 경우을 사용 tr '\n' '\0' <a.txt | xargs -0 ...하지 않고 줄 지향 읽기를 얻을 수 있습니다 -d).

  • _플레이스 홀더이기 $0등이 추가로 다른 데이터 값 xargs이 될 $1가치관의 기본 설정되는 일이있는 전방 및 for루프 반복 처리.


58
익숙하지 않은 사용자의 경우 sh -c-각 명령 이후의 세미콜론은 목록의 마지막 명령 인 경우에도 선택 사항이 아닙니다.
노아 서스 먼

6
적어도 내 구성에서는 초기 "{"바로 뒤에 공백이 있어야합니다. 끝나는 중괄호 앞에는 공간이 필요하지 않지만 Sussman 씨가 지적한 것처럼 닫는 세미콜론이 필요합니다.
willdye

4
이 대답은 이전에 중괄호했다 command1command2; 나중에 그들이 필요하지 않다는 것을 깨달았습니다.
Keith Thompson

24
세미콜론에 대한 위의 설명을 명확히하려면 닫기 전에 세미콜론이 필요합니다 }. sh -c '{ command1; command2; }' -- but it's not required at the end of a command sequence that doesn't use braces: sh -c 'command1; command2'`
Keith Thompson

8
당신이 포함하는 경우 %에 전달하여 문자열에서 문자 어딘가를 sh -c파일 이름이 포함 : 다음이 보안 취약점 경향이있다 $(rm -rf ~)'$(rm -rf ~)'(그리고 일반적인 UNIX 파일 시스템에서 파일 이름에서 가지고 완벽하게 법적 문자열입니다!) 누군가가 원인이됩니다 매우 나쁜 일 .
찰스 더피

35

GNU Parallel을 사용하면 다음을 수행 할 수 있습니다.

cat a.txt | parallel 'command1 {}; command2 {}; ...; '

자세한 내용은 소개 동영상을 참조하십시오. https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

보안상의 이유로 패키지 관리자를 사용하여 설치하는 것이 좋습니다. 그러나 그렇게 할 수 없다면이 10 초 설치를 사용할 수 있습니다.

10 초 동안 설치하면 전체 설치를 시도합니다. 실패하면 개인 설치; 실패하면 최소 설치입니다.

$ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
   fetch -o - http://pi.dk/3 ) > install.sh
$ sha1sum install.sh | grep 3374ec53bacb199b245af2dda86df6c9
12345678 3374ec53 bacb199b 245af2dd a86df6c9
$ md5sum install.sh | grep 029a9ac06e8b5bc6052eac57b2c3c9ca
029a9ac0 6e8b5bc6 052eac57 b2c3c9ca
$ sha512sum install.sh | grep f517006d9897747bed8a4694b1acba1b
40f53af6 9e20dae5 713ba06c f517006d 9897747b ed8a4694 b1acba1b 1464beb4
60055629 3f2356f3 3e9c4e3c 76e3f3af a9db4b32 bd33322b 975696fc e6b23cfb
$ bash install.sh

56
알 수없는 사이트에서 임의의 스크립트를 실행하여 도구를 설치하는 것은 끔찍한 관행입니다. 쉬 ... | (일부 연장에) 병렬 랜덤 wget을보다 더 많은 방법을 신뢰할 수있는 인기있는 배포판에 대한 oficiall 패키지가
mdrozdziel

4
가장 쉬운 공격 경로는 무엇입니까? Pi.dk는 GNU Parallel의 저자에 의해 제어되므로 서버에 침입하거나 DNS를 인수해야하는 공격이 있습니다. 배포판의 공식 패키지를 인수하기 위해 종종 패키지를 유지하기 위해 자원 봉사 할 수 있습니다. 따라서 당신이 일반적으로 옳을 수도 있지만,이 특별한 경우에는 귀하의 의견이 정당하지 않은 것 같습니다.
Ole Tange

10
실제로 나는 pi.dk가 저자의 것임을 모른다. 실제로 이것이 사실인지 확인하고 wget에서 ssl을 사용하는 방법을 생각 하고이 명령이 수행 해야하는 작업을 수행하는지 확인하는 것은 약간의 작업입니다. 공식 패키지에 악성 코드가 포함될 수 있다는 것은 사실이지만 wget 패키지에도 적용됩니다.
Fabian

3
OP가 실행하려는 각 명령이 순차적이어야한다면 이것이 최선의 해결책이 아닐 수 있습니다.
IcarianComplex

2
@IcarianComplex -j1을 추가하면 문제가 해결됩니다.
Ole Tange

26

이것은 xargs 또는 cat이없는 또 다른 접근법입니다.

while read stuff; do
  command1 "$stuff"
  command2 "$stuff"
  ...
done < a.txt

2
주어진대로. 을 지우지 않으면 IFS파일 이름에서 앞뒤 공백을 무시합니다. 를 추가하지 않으면 -r리터럴 백 슬래시가있는 파일 이름은 해당 문자를 무시합니다.
Charles Duffy

질문에 대답하지 않습니다. 에 대해 구체적으로 물었습니다 xargs. (이것은 GNU 비슷한 할 확장하기 어렵다 xargs' -P<n>옵션)
버그 덴 거트 밴을

1
이것은 완벽하게 작동합니다. 당신은 또한 같은 파이프 명령으로 사용할 수 있습니다$ command | while read line; do c1 $line; c2 $line; done
Alexar

25

당신이 사용할 수있는

cat file.txt | xargs -i  sh -c 'command {} | command2 {} && command3 {}'

{} = 텍스트 파일의 각 줄에 대한 변수


8
이것은 안전하지 않습니다. 무엇 당신이 경우 file.txt에 데이텀을 포함 $(rm -rf ~)문자열로?
찰스 더피

19

내가하는 한 가지는 .bashrc / .profile 에이 기능을 추가하는 것입니다.

function each() {
    while read line; do
        for f in "$@"; do
            $f $line
        done
    done
}

다음과 같은 일을 할 수 있습니다

... | each command1 command2 "command3 has spaces"

xargs 또는 -exec보다 덜 장황합니다. 또한 해당 동작이 필요한 경우 명령의 임의 위치에서 읽은 값을 각 위치에 삽입하도록 함수를 수정할 수도 있습니다.


1
과소 평가, 이것은 매우 편리합니다
charlesreid1

15

나는 드라이 런 모드를 허용하는 스타일을 선호한다 | sh.

cat a.txt | xargs -I % echo "command1; command2; ... " | sh

파이프와도 작동합니다.

cat a.txt | xargs -I % echo "echo % | cat " | sh

2
당신이 GNU의 xargs를 '사용할 때까지이 작품은, -P옵션을 ... (그렇지 않은 경우, 내가 주로 사용 -exec에 대한 find내 입력은 대부분 파일 이름이기 때문에,)
거트 반 베르그 덴

13

파티에 조금 늦었 어

마이그레이션하기 전에 수천 개의 작은 파일로 디렉토리를 압축하는 데 아래 형식을 사용합니다. 명령 안에 작은 따옴표가 필요하지 않으면 작동합니다.

약간의 수정으로 누군가에게 유용 할 것이라고 확신합니다. Cygwin(바분) 에서 테스트

find . -maxdepth 1 ! -path . -type d -print0 | xargs -0 -I @@ bash -c '{ tar caf "@@.tar.lzop" "@@" && echo Completed compressing directory "@@" ; }'

find .여기 찾기
-maxdepth 1하위 디렉토리로 이동하지 마십시오
! -path .제외합니다. / 현재 디렉토리 경로
-type d만 디렉토리와 일치합니다.
-print0null 바이트로 분리 된 출력 \ 0
| xargsxargs로 파이프
-0입력이 널로 분리 된 바이트입니다.
-I @@자리 표시자는 @@입니다. @@을 입력으로 바꿉니다.
bash -c '...'Bash 명령 실행
{...}명령 그룹화
&& 이전 명령이 성공적으로 종료 된 경우에만 다음 명령을 실행 (종료 0).

결승 ;은 중요합니다. 그렇지 않으면 실패합니다.

산출:

Completed compressing directory ./Directory1 with meta characters in it
Completed compressing directory ./Directory2 with meta characters in it
Completed compressing directory ./Directory3 with meta characters in it

2018 년 7 월 업데이트 :

해킹과 장난을 좋아한다면 흥미로운 것이 있습니다.

echo "a b c" > a.txt
echo "123" >> a.txt
echo "###this is a comment" >> a.txt
cat a.txt
myCommandWithDifferentQuotes=$(cat <<'EOF'                                     
echo "command 1: $@"; echo 'will you do the fandango?'; echo "command 2: $@"; echo
EOF
)
< a.txt xargs -I @@ bash -c "$myCommandWithDifferentQuotes" -- @@

산출:

command 1: a b c
will you do the fandango?
command 2: a b c

command 1: 123
will you do the fandango?
command 2: 123

command 1: ###this is a comment
will you do the fandango?
command 2: ###this is a comment

설명 :
- 하나의 라이너 스크립트를 작성하고 변수에 저장
- xargs 읽어 a.txt하고 실행하는 그것을 bash스크립트
- @@ 확인 전체 라인이 통과 될 때마다한다
- 퍼팅 @@--확인합니다 @@에 위치 매개 변수 입력으로 촬영 bash명령이 아닌 bash시작 OPTION, 즉, 추천 -c자체하는 수단run command

--마법, 그것은 즉, 다른 많은 것들, 작동 ssh도,kubectl


1
이런 종류의 설정을 사용했습니다 : find . -type f -print0|xargs -r0 -n1 -P20 bash -c 'f="{}";ls -l "$f"; gzip -9 "$f"; ls -l "$f.gz"'(루프를 변환 할 때 약간 더 쉽습니다)
Gert van den Berg

1
내 이전 의견에 대한 늦은 편집 : (파일 이름에 큰 따옴표가 포함되어 있으면 보안 문제가 있습니다 ...) (사용하는 것이 이것을 "$@"피할 수있는 유일한 방법 인 것 같습니다 ... ( -n1매개 변수의 수를 제한하려는 경우))
게르트 반 덴 버그

주목해야한다 --더 이상 옵션을 허용 할 수 없다는 말을 쉘에 의해 사용된다. 이것은 -이후에도 있을 수 있습니다 --. grep -r패턴에 포함시킬 때 와 같이하지 않으면 매우 흥미롭고 혼란스러운 결과를 얻을 수 있습니다 -! 당신이 그것을 말한 방법이 이것을 명확히하지는 않지만 이것이 어떻게 작동하는지 실제로 설명하지는 않습니다. Iirc 그것은 POSIX 일이지만 어쨌든 이것을 지적 할 가치가 있다고 생각합니다. 고려해야 할 것. 그리고 나는 그 보너스 btw를 좋아합니다!
Pryftan

10

가장 안전한 버전 인 것 같습니다.

tr '[\n]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'command1 "$@"; command2 "$@";' ''

( -0제거하고 tr리디렉션으로 대체 할 수 있습니다 (또는 파일을 null로 구분 된 파일로 대체 할 수 있습니다). 주로 출력 xargsfind함께 사용하기 때문에 주로 존재 -print0합니다) ( 확장자가 xargs없는 버전 에서도 관련이있을 수 있습니다 -0)

args는 매개 변수를 실행할 때 매개 변수를 셸에 배열로 전달하므로 안전합니다. 쉘은 (적어도 bash) 다음을 사용하여 모두 얻을 때 변경되지 않은 배열로 다른 프로세스에 전달합니다.["$@"][1]

을 사용 ...| xargs -r0 -I{} bash -c 'f="{}"; command "$f";' ''하면 문자열에 큰 따옴표가 있으면 할당이 실패합니다. 이것은 모든 변종이 사용하는 사실 -i또는-I . (문자열로 대체되기 때문에 항상 예상치 못한 문자 (따옴표, 역 따옴표 또는 달러 기호)를 입력 데이터에 삽입하여 명령을 삽입 할 수 있습니다)

명령이 한 번에 하나의 매개 변수 만 사용할 수있는 경우 :

tr '[\n]' '[\0]' < a.txt | xargs -r0 -n1 /bin/bash -c 'command1 "$@"; command2 "$@";' ''

또는 프로세스가 다소 적습니다.

tr '[\n]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'for f in "$@"; do command1 "$f"; command2 "$f"; done;' ''

확장 기능 이있는 GNU xargs또는 다른 -P확장 프로그램이 있고 32 개의 프로세스를 병렬로 실행하려는 경우 각 명령마다 매개 변수가 10 개를 넘지 않아야합니다.

tr '[\n]' '[\0]' < a.txt | xargs -r0 -n10 -P32 /bin/bash -c 'command1 "$@"; command2 "$@";' ''

이것은 입력의 특수 문자에 대해 강력해야합니다. (입력이 널로 분리 된 경우)tr 경우) 일부 행에 개행이 포함되어 있으면 버전에서 유효하지 않은 입력을 받지만 개행으로 분리 된 파일에서는 피할 수 없습니다.

빈 첫 번째 매개 변수 bash -c는 다음과 같습니다. ( bashMan 페이지에서 ) (Thanks @clacke)

-c   If the -c option is present, then  commands  are  read  from  the  first  non-option  argument  com
     mand_string.   If there are arguments after the command_string, the first argument is assigned to $0
     and any remaining arguments are assigned to the positional parameters.  The assignment  to  $0  sets
     the name of the shell, which is used in warning and error messages.

이것은 파일 이름에 큰 따옴표로도 작동합니다. 제대로 지원하는 쉘이 필요합니다"$@"
Gert van den Berg

bash에 대한 argv [0] 인수가 누락되었습니다. bash -c 'command1 "$@"; command2 "$@";' arbitrarytextgoeshere
clacke

3
이것은 xargs가하는 일이 아닙니다. bashwith -c는 명령 이름 뒤에 프로세스 이름이 될 하나의 인수를 먼저 취한 다음 위치 인수를 사용합니다. bash -c 'echo "$@" ' 1 2 3 4무엇이 나오는지 보십시오 .
clacke

Bobby-Tabled를 얻지 못하는 안전한 버전을 사용하는 것이 좋습니다.
Mateen Ulhaq

8

나를 위해 작동하는 또 다른 가능한 해결책은 다음과 같습니다.

cat a.txt | xargs bash -c 'command1 $@; command2 $@' bash

끝에 'bash'를 주목하십시오-나는 그것이 argv [0]으로 bash에 전달된다고 가정합니다. 이 구문이 없으면 각 명령의 첫 번째 매개 변수가 손실됩니다. 어떤 단어라도 될 수 있습니다.

예:

cat a.txt | xargs -n 5 bash -c 'echo -n `date +%Y%m%d-%H%M%S:` ; echo " data: " $@; echo "data again: " $@' bash

5
을 인용하지 않으면 "$@"인수 목록을 문자열 분할하고 glob-expand하는 것입니다.
찰스 더피

2

이것에 대한 나의 현재 BKM은

... | xargs -n1 -I % perl -e 'system("echo 1 %"); system("echo 2 %");'

불행히도 이것은 bash를 사용하는 펄을 사용하는 것이 불행하다. 그러나 허용 된 답변보다 더 많은 입력을 처리합니다. (펄에 의존하지 않는 유비쿼터스 버전을 환영합니다.)

@KeithThompson의 제안

 ... | xargs -I % sh -c 'command1; command2; ...'

입력에 쉘 주석 문자 #가 없으면, 첫 번째 명령의 일부와 두 번째 명령이 모두 잘립니다.

입력이 ls 또는 find와 같은 파일 시스템 목록에서 파생되고 편집기에서 이름이 # 인 임시 파일을 작성하는 경우 해시 #은 매우 일반적 일 수 있습니다.

문제의 예 :

$ bash 1366 $>  /bin/ls | cat
#Makefile#
#README#
Makefile
README

죄송합니다. 여기 문제가 있습니다.

$ bash 1367 $>  ls | xargs -n1 -I % sh -i -c 'echo 1 %; echo 2 %'
1
1
1
1 Makefile
2 Makefile
1 README
2 README

아아, 그게 낫다 :

$ bash 1368 $>  ls | xargs -n1 -I % perl -e 'system("echo 1 %"); system("echo 2 %");'
1 #Makefile#
2 #Makefile#
1 #README#
2 #README#
1 Makefile
2 Makefile
1 README
2 README
$ bash 1369 $>  

5
# 문제는 따옴표를 사용하여 쉽게 해결할 수 있습니다.ls | xargs -I % sh -c 'echo 1 "%"; echo 2 "%"'
gpl
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.