파이프의 두 번째 측면에 Xargs가 있습니까?

파이프의 두 번째 측면에 Xargs가 있습니까?

나는 다음을 수행하려고합니다 :

cat file1.txt | xargs -I{} "cat file2.txt | grep {}"

file1의 각 줄이 세 번째 파이프 끝의 grep 값이 될 것으로 예상합니다. 예상대로 작동하지 않습니다.

-I{}파이프에 닿으면 교체할 물건을 찾는 것을 중단하기 때문입니까 ? 이 문제를 해결할 방법이 있나요?

답변1

파이프를 생성하거나 리디렉션을 수행하려면 쉘이 필요하기 때문입니다. 이는 cat연결 명령이므로 하나의 파일에만 사용하는 것은 거의 의미가 없습니다.

cat file1.txt | xargs -I{} sh -c 'cat file2.txt | grep -e "$1"' sh {}

하다~ 아니다하다:

고양이 파일1.txt | xargs -I{} sh -c 'cat file2.txt | grep -e {}'

이는 명령 주입 취약점에 해당합니다. {}코드 인수에서 확장되어 쉘 sh코드로 해석됩니다. 예를 들어, 라인 중 하나가 file1.txtwas $(reboot)라면 를 호출할 것입니다 reboot.

(또는 을 -e사용할 수도 있음 --)도 중요합니다. 그것이 없으면 정규 표현식이 -.

다음 대신 리디렉션을 사용하여 위 작업을 단순화할 수 있습니다 cat.

< file1.txt xargs -I{} sh -c '< file2.txt grep -e "$1"' sh {}

또는 리디렉션을 사용하는 대신 파일 이름을 인수로 전달하면 grep다음을 삭제할 수도 있습니다 sh.

< file1.txt xargs -I{} grep -e {} file2.txt

grep단일 호출로 모든 정규 표현식을 한 번에 찾도록 지시할 수도 있습니다 .

grep -f file1.txt file2.txt

그러나 이 경우에는 의 각 줄에 대한 하나의 정규 표현식일 뿐이며 file1.txt에서 수행하는 특별한 인용 처리는 없습니다 xargs.

xargs기본적으로 입력은 공백(일부 구현에서는 공백과 탭만, 다른 구현에서는 [:blank:]현재 로케일의 문자 클래스에 있음) 또는 백슬래시와 작은따옴표 및 큰따옴표를 사용하여 구분 기호를 이스케이프할 수 있는 개행으로 구분된 단어 목록으로 간주됩니다. (개행은 백슬래시로만 이스케이프할 수 있습니다) 또는 서로.

예를 들어 다음과 같은 입력에 대해

 'a "b'\" "bar baz" x\
y

xargs없이는 -I{}pass a "b"하고 명령에 전달합니다 bar baz.x<newline>y

를 사용하면 -I{}xargs줄에 한 단어를 가져오지만 여전히 추가 처리를 수행합니다. 선행(후행 아님) 공백은 무시합니다. 공백은 더 이상 구분 기호로 간주되지 않지만 견적 처리는 계속 수행됩니다.

위 입력에서는 하나의 인수를 명령에 xargs -I{}전달합니다 . a "b" foo bar x<newline>y또한 POSIX에서 요구하는 많은 시스템 중 하나는 단어 길이가 255자를 초과하면 작동하지 않는다는 점에 유의하세요. 대체로 xargs -I{}쓸모가 없습니다.

각 행을 명령에 대한 인수로 그대로 전달하려면 GNU xargs -d '\n'확장을 사용할 수 있습니다.

< file1.txt xargs -d '\n' -n 1 grep file2.txt -e

(여기서는 인수 뒤에 옵션을 전달할 수 있는 GNU의 또 다른 확장에 의존합니다 grep(POSIX가 올바른 환경에 없는 경우) 또는 이식 가능:

sed "s/'/'\\\\\\''/g;s/.*/'&'/" file1.txt | xargs -n1 sh -c '
  for line do
    grep -e "$line" file2.txt
  done' sh

각각 원하셨다면단어in file1.txt(인용문은 여전히 ​​인식됨) 각각의 반대찾으려면 (어쨌든 한 줄에 단어가 하나 있으면 후행 공백 문제를 해결할 수도 있음) 다음을 xargs -n1사용하는 대신 단독으로 사용할 수 있습니다 -I.

< file1.txt xargs -n1 sh -c '
  for word do
    grep -e "$word" file2.txt
  done' sh

선행 및 후행 공백을 제거하려면(그러나 인용 처리는 xargs수행하지 않음) 다음을 수행할 수도 있습니다.

unset IFS # restore word splitting to its default
while read -r regexp; do
  grep -e "$regexp" file2.txt
done < file1.txt

답변2

수행하려는 작업에 따라 xargs전체를 건너뛰고 대신 다음 솔루션을 사용하는 것이 더 나을 수 있습니다.

grep -f file1.txt file2.txt

이는 원래 명령과 다릅니다.(Stéphane Chazelas의 답변에서와 같이 수정하면) 다음과 같습니다.

  • file2.txt선은 일치하는 패턴에 관계없이 나타나는 순서대로 인쇄됩니다 . 명령에서 첫 번째 패턴과 일치하는 모든 줄이 인쇄된 다음 두 번째 패턴과 일치하는 모든 줄이 인쇄됩니다.
  • 둘 이상의 패턴과 일치하는 라인은 정확히 한 번 인쇄됩니다. 명령에서는 일치하는 각 패턴에 대해 한 번 인쇄됩니다.
  • -v및 를 포함하여 여러 플래그를 더 쉽게 사용할 수 있습니다 -c.

깃발 -fPOSIX에 의해 지정됨따라서 합리적으로 휴대 가능합니다.

관련 정보