명령 대체를 통해 다른 명령에 인수를 생성하는 방법


11

:에 이어 쉘 명령어 치환에서 예기치 않은 동작

나는 거대한 인수 목록을 취할 수있는 명령을 가지고 있는데, 그중 일부는 합법적으로 공백을 포함 할 수 있습니다 (아마도 다른 것들)

나는 따옴표로 나를 위해 그러한 인수를 생성 할 수있는 스크립트를 작성했지만 출력을 복사하여 붙여 넣어야합니다.

./somecommand
<output on stdout with quoting>
./othercommand some_args <output from above>

나는 단순히 그렇게함으로써 이것을 간소화하려고 노력했다.

./othercommand $(./somecommand)

위의 질문에서 언급 된 예기치 않은 동작이 발생했습니다. 문제는- othercommand일부 인수에 인용이 필요하고이를 변경할 수 없다는 점에서 명령 대체를 안정적으로 사용하여 인수를 생성 할 수 있습니까?


"신뢰할 수있는"의 의미에 따라 다릅니다. 다음 명령이 화면에 표시된대로 정확하게 출력을 가져 와서 쉘 규칙을 적용하려면 eval사용할 수 있지만 일반적으로 권장하지는 않습니다. xargs뭔가뿐만 아니라 고려하는 것입니다
세르지 Kolodyazhnyy

somecommand정기적 인 셸 구문 분석을 수행하기 위한 출력을 원합니다 (사용자 예상)
user1207217

내 대답에서 말했듯이 필드 분할 (예 :)에 다른 문자를 사용 :하여 문자가 안정적으로 출력 되지 않을 것이라고 가정합니다 .
Olorin

그러나 인용 규칙을 따르지 않기 때문에 이것은 실제로 옳지 않습니다. 이것이 사용자에 관한 것입니다
user1207217

2
실제 사례를 게시 할 수 있습니까? 첫 번째 명령의 실제 출력과 두 번째 명령과의 상호 작용 방법을 의미합니다.
nxnev

답변:


10

나는 인용문과 함께 나를 위해 그러한 인수를 생성 할 수있는 스크립트를 작성했습니다.

쉘에 대한 출력이 올바르게 인용 되고 출력을 신뢰하면 실행할 수 있습니다 eval.

배열을 지원하는 쉘이 있다고 가정하면, 인수를 저장하기 위해 하나를 사용하는 것이 가장 좋습니다.

경우 ./gen_args.sh출력을 같은 생산 'foo bar' '*' asdf, 우리는 실행할 수 eval "args=( $(./gen_args.sh) )"라는 배열 채우는 args결과와 함께합니다. 즉, 세 가지 요소가 될 것이다 foo bar, *, asdf.

"${args[@]}"평소처럼 배열 요소를 개별적으로 확장 할 수 있습니다 :

$ eval "args=( $(./gen_args.sh) )"
$ for var in "${args[@]}"; do printf ":%s:\n" "$var"; done
:foo bar:
:*:
:asdf:

( "${array[@]}"따옴표는 수정되지 않은 별도의 인수로 모든 요소로 확장됩니다. 따옴표없이 배열 요소는 단어 분할의 대상이됩니다. 예를 들어 BashGuide배열 페이지를 참조하십시오 .)

그러나 , eval행복하게 때문에, 어떤 쉘 대체를 실행 $HOME출력의 홈 디렉토리로 확장 것이고, 명령 치환 실제로 쉘 실행에서 명령을 실행하는 것입니다 eval. 출력은 "$(date >&2)"하나의 빈 배열 요소를 만들고 stdout에 현재 날짜를 인쇄합니다. gen_args.sh네트워크를 통한 다른 호스트와 같이 신뢰할 수없는 소스에서 데이터를 가져 오는 경우 다른 사용자가 만든 파일 이름이 문제가됩니다. 출력에는 임의의 명령이 포함될 수 있습니다. ( get_args.sh자체가 악의적 인 경우 아무 것도 출력 할 필요가 없으며 악의적 인 명령을 직접 실행할 수 있습니다.)


eval없이 구문 분석하기 어려운 쉘 인용의 대안은 스크립트 출력에서 ​​다른 문자를 구분 기호로 사용하는 것입니다. 실제 주장에 필요하지 않은 것을 선택해야합니다.

를 선택 #하고 스크립트 출력을 갖도록 합시다 foo bar#*#asdf. 이제 인용되지 않은 명령 확장을 사용 하여 명령의 출력을 인수로 분할 할 수 있습니다 .

$ IFS='#'                          # split on '#' signs
$ set -f                           # disable globbing
$ args=( $( ./gen_args3.sh ) )     # assign the values to the arrayfor var in "${args[@]}"; do printf ":%s:\n" "$var"; done
:foo bar:
:*:
:asdf:

IFS스크립트의 다른 부분에서 단어 분리에 의존하는 경우 나중에 다시 설정해야하며 ( unset IFS기본값으로 설정해야 함) set +f나중에 globbing을 사용하려는 경우 에도 사용해야 합니다.

Bash 또는 배열이있는 다른 쉘을 사용하지 않는 경우 해당 위치 매개 변수를 사용할 수 있습니다. 대신 교체 args=( $(...) )하여 set -- $(./gen_args.sh)사용 하십시오 . (여기에도 따옴표가 필요합니다 . 그렇지 않으면 위치 매개 변수에 단어 분할이 적용됩니다.)"$@""${args[@]}""$@"


두 세계의 최고!
Olorin

당신은 인용의 중요성을 보여주는 발언 추가 할 수 ${args[@]}그렇지 않으면이 작동하지 않았다 날을 -
user1207217

@ user1207217, 그렇습니다. 그것은 배열과 같은 일이 "${array[@]}"와 같이 "$@". 둘 다 인용해야하거나 그렇지 않으면 단어 분리로 인해 배열 요소가 부분으로 나뉩니다.
ilkkachu 2018 년

6

문제는 somecommand스크립트가에 대한 옵션을 출력하면 옵션 othercommand은 실제로 텍스트이며 쉘의 표준 구문 분석에 $IFS달려 있습니다 (무슨 일이 일어나고 어떤 쉘 옵션이 영향을 받는지에 따라 영향을받습니다 . 일반적인 경우에는 그렇지 않습니다) 제어).

옵션 somecommand출력 하는 데 사용 하는 대신을 사용하여 호출 하는 것이 더 쉽고 안전하며 강력합니다 othercommand. somecommand스크립트는 다음 될 것 래퍼 스크립트 의 주위에 othercommand당신의 명령 행의 일부로서 어떤 특별한 방법으로 전화를해야하는 단점이 대신 도우미 스크립트의 일종 otherscript. 래퍼 스크립트는 다른 옵션 세트로 다른 유사한 도구를 호출하는 도구를 제공하는 매우 일반적인 방법입니다 ( 실제로 쉘 스크립트 래퍼 file에있는 명령을 확인하십시오 /usr/bin).

에서 bash, ksh또는 zsh, 당신은 쉽게 배열을 사용하는 래퍼 스크립트에 대한 개별 옵션 잡아 수 othercommand있도록 등에 :

options=( "hi there" "nice weather" "here's a star" "*" )
options+=( "bonus bumblebee!" )  # add additional option

그런 다음 othercommand래퍼 스크립트 내에서 호출하십시오 .

othercommand "${options[@]}"

의 확장은 배열 "${options[@]}"의 각 요소 options가 개별적으로 인용되고 othercommand별도의 인수로 표시되도록 합니다.

래퍼의 사용자는 실제로 호출하고 있다는 사실을 알지 못합니다 . 스크립트가 대신 명령 행 옵션을 출력으로 생성 한 경우에는 사실 othercommand아닙니다othercommand .

에서을 /bin/sh사용 $@하여 옵션을 유지하십시오.

set -- "hi there" "nice weather" "here's a star" "*"
set -- "$@" "bonus bumblebee!"  # add additional option

othercommand "$@"

( set위치 파라미터를 설정하기 위해 사용되는 명령이다 $1, $2, $3등이 어떻게 배열 구성하는있는 $@초기 표준 POSIX 쉘을. --에 신호를하는 것입니다 set주어진 어떤 옵션 인수 만이 없는지. --되어 정말 에만 경우 필요 첫 번째 값은 -)로 시작하는 것 입니다.

큰 따옴표 $@이며 ${options[@]}요소가 개별적으로 단어 분리되지 않고 파일 이름이 붙지 않도록합니다.


설명해 주 set --시겠습니까?
user1207217

@ user1207217 답변 설명이 추가되었습니다.
Kusalananda

4

는 IF somecommand출력이 안정적으로 좋은 쉘 구문에, 당신은 사용할 수 있습니다 eval:

$ eval sh test.sh $(echo '"hello " "hi and bye"')
hello 
hi and bye

그러나 출력에 유효한 인용이 있는지 확인해야합니다. 그렇지 않으면 스크립트 외부에서 명령을 실행할 수도 있습니다.

$ cat test.sh 
for var in "$@"
do
    echo "|$var|"
done
$ ls
bar  baz  test.sh
$ eval sh test.sh $(echo '"hello " "hi and bye"; echo rm *')
|hello |
|hi and bye|
rm bar baz test.sh

echo rm bar baz test.sh때문에 스크립트로 전달되지 않았으며 ;별도의 명령으로 실행되었습니다. 이것을 강조하기 위해 |주변 $var을 추가했습니다 .


일반적으로의 출력을 완전히 신뢰할 수 없으면 출력 somecommand을 안정적으로 사용하여 명령 문자열을 작성할 수 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.