xargs가 마지막 인수를 처리하지 않는 이유는 무엇입니까?

xargs가 마지막 인수를 처리하지 않는 이유는 무엇입니까?

관찰하다:

mark@L-R910LPKW:~$ echo a b | xargs -d' ' -I{} bash -c 'echo {} 1'
a 1
b
bash: line 2: 1: command not found
mark@L-R910LPKW:~$

무슨 일이야?

답변1

뭔가 잘못됐어

b출력에 나타나므로 처리되었지만 예상한 방식이 아닙니다.

첫 번째 단계로 bash가 무엇을 보는지 알려줍니다. -x추적을 활성화하는 옵션을 전달합니다.

$ echo a b | xargs -d' ' -I{} bash -x -c 'echo {} 1'
+ echo a 1
a 1
+ echo b
b
+ 1
bash: line 2: 1: command not found

echo a 1따라서 예상대로 bash가 먼저 호출됩니다. 하지만 다음 줄은 당신이 생각하는 것처럼 보이지 echo b않습니다 . echo b 1여분의 줄이 있습니다 1. 왜?

글쎄, xargs에게 공백으로 분할하라고 지시합니다. 개행 문자가 있는 a b␤입력 을 전달했습니다 . 따라서 xargs는 입력에 a및 2개의 조각이 포함되어 있음을 확인합니다 b␤. 지시에 따라 xargs는 각 조각에서 bash를 먼저 호출한 echo a 1다음 실행합니다 echo b␤1.

일을 올바르게 하는 방법

find의 일부 버전에서는 셸 조각을 xargs포함할 수 있습니다 {}. 이는 거의 항상 나쁜 생각이며 일부 파일 이름이나 기타 데이터를 손상시키고 일반적으로 보안 허점이 됩니다. 데이터를 별도의 매개변수로 전달합니다.

"find -exec sh -c"를 사용해도 안전합니까?

답변2

~처럼Giles는 그의 답변에서 언급했습니다., -d ' 'GNU xargs에 대한 옵션을 사용하면 공백과 공백만 구분 기호로 간주하여 줄 바꿈을 데이터의 일부로 남겨두고 문자 자체처럼 쉘 코드에 포함됩니다. 그것은 아마도 당신이 원하는 것이 아닐 것입니다. (가장 일반적인 용도 는 예를 들어 추가 처리 없이 행을 있는 그대로 사용하도록 지시하는 것 -d입니다 .)-d '\n'-L

대신 공백으로 구분된 각 단어를 별도의 항목으로 만들려는 경우 한 가지 옵션은 공백에서 항목을 분할하는 기본 동작을 활용하는 것이므로 다음과 같이 직접 작동합니다.

$ echo a b | xargs -n1 bash -c 'echo "$1" 1' sh
a 1
b 1

따옴표와 백슬래시도 처리하므로(셸과 약간 다름) 공백을 구분 기호로 사용하는 것과는 다릅니다. 입력은 a "b c"항목을 생성 a하고 b c.

또는 with를 사용하여 입력을 전처리하고 구분 기호로 사용하려는 모든 문자를 하나의 문자로 축소할 수 -d있습니다 tr.

$ printf 'a b\nc\n' | tr ' ' '\n' | xargs -d'\n' -n1 bash -c 'echo "$1" 1' sh
a 1
b 1
c 1

그러나 -d이것은 표준이 아니며 GNU xargs에서만 구현되는 것으로 생각됩니다. 따라서 -0대신 더 폭넓은 지원을 제공하는 것을 사용하는 것이 좋습니다.

$ printf 'a b\nc\n' | tr ' \n' '\0' | xargs -0 -n1 bash -c 'echo "$1" 1' sh
a 1
b 1
c 1

그럼에도 불구하고 쉘 조각에 값을 직접 삽입하지 마십시오. 이는 안전하지 않으며 임의의 값으로 올바르게 작동하는 것이 불가능하기 때문입니다.

관련 정보