명령 대체를 통해 다른 명령의 매개변수를 생성하는 방법

명령 대체를 통해 다른 명령의 매개변수를 생성하는 방법

다음에서: 쉘 명령 대체의 예기치 않은 동작

많은 수의 인수를 허용하는 명령이 있습니다. 그 중 일부는 합법적으로 공백(및 아마도 다른 것)을 포함할 수 있습니다.

따옴표를 사용하여 이러한 매개변수를 생성하는 스크립트를 작성했지만 출력을 복사하여 붙여넣어야 합니다.

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

나는 이것을 간단하게 단순화하려고 노력했습니다.

./othercommand $(./somecommand)

위에서 언급한 예상치 못한 동작이 발생했습니다. 문제는 othercommand일부 매개변수에 인용이 필요하고 변경할 수 없는 경우 매개변수를 생성하기 위해 명령 대체를 안정적으로 사용할 수 있는가입니다.

답변1

나는 따옴표를 사용하여 이러한 매개변수를 생성하는 스크립트를 작성했습니다.

쉘의 출력이 올바르게 인용되면,그리고 당신은 그 결과를 신뢰합니다, 그런 다음 실행할 수 있습니다 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)"빈 배열 요소를 생성하고 표준 출력에 현재 날짜를 인쇄합니다. 이는 gen_args.sh신뢰할 수 없는 소스(예: 네트워크의 다른 호스트, 다른 사용자가 만든 파일 이름)에서 데이터를 얻은 경우 문제가 됩니다.출력에는 임의의 명령이 포함될 수 있습니다.( get_args.sh악성이라면 아무 것도 출력할 필요 없이 악성 명령어를 직접 실행하면 됩니다.)


쉘 인용의 대안은 스크립트 출력에서 ​​다른 문자를 구분 기호로 사용하는 것입니다. 쉘 인용은 평가 없이 구문 분석하기 어렵기 때문입니다. 실제 매개변수에서는 필요하지 않은 것을 선택해야 합니다.

선택 #하고 스크립트를 출력해 보겠습니다 foo bar#*#asdf. 이제 우리는 사용할 수 있습니다인용되지 않음명령 확장은 명령의 출력을 매개변수로 분할합니다.

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

IFS스크립트의 다른 곳에서 단어 분리를 사용하는 경우( unset IFS기본값으로 설정해야 함) 나중에 이를 설정해야 하며, set +f나중에 와일드카드를 사용하려면 와일드카드도 사용할 수 있습니다.

Bash나 배열이 포함된 다른 셸을 사용하지 않는 경우 위치 인수를 사용할 수 있습니다. 대신 then args=( $(...) )으로 바꾸고 사용하세요 . (여기에서도 주위에 따옴표가 필요합니다 . 그렇지 않으면 위치 매개변수가 단어 분리의 영향을 받습니다.)set -- $(./gen_args.sh)"$@""${args[@]}""$@"

답변2

문제는 somecommand스크립트가 options 을 출력 하면 othercommand해당 옵션은 실제로는 텍스트일 뿐이고 쉘의 표준 구문 분석( $IFS현재 진행 중인 작업과 적용되는 쉘 옵션에 영향을 받습니다. 일반적인 경우에는아니요제어할 수 있음).

somecommand사용하는 대신산출옵션을 사용하면 더 쉽고 안전하며 강력해집니다.부르다 othercommand. 스크립트 somecommand는 다음과 같습니다.래퍼 스크립트래퍼 스크립트 는 다른 옵션 세트와 함께 다른 유사한 도구를 호출하는 도구를 제공하는 매우 일반적인 방법입니다 othercommand. 내부 명령은 실제로 쉘 스크립트 래퍼입니다).otherscriptfile/usr/bin

bash또는 에서는 배열을 사용하여 다음과 같이 개별 옵션 kshzsh보유하는 래퍼 스크립트를 쉽게 만들 수 있습니다 othercommand.

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

그런 다음 othercommand(여전히 래퍼 스크립트에 있음) 다음을 호출합니다.

othercommand "${options[@]}"

확장을 통해 "${options[@]}"배열의 각 요소가 개별적으로 참조되고 별도의 인수로 표시됩니다 options.othercommand

래퍼 사용자는 실제로 를 호출하고 있다는 사실을 잊어버릴 것입니다 othercommand.아니요스크립트가 단순히 명령줄 옵션을 출력으로 생성하는 경우 othercommandTrue입니다 .

에서 옵션을 저장 /bin/sh하려면 다음 $@을 수행하세요.

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

othercommand "$@"

( 위치 인수 등을 set설정하는 데 사용되는 명령입니다 . 이는 표준 POSIX 쉘의 배열 내용입니다 . 첫 번째 문자는 옵션이 없고 인수만 있음을 나타냅니다. 예$1$2$3$@--set--진짜첫 번째 값이 )로 시작하는 경우에만 필요합니다 -.

요소가 개별적으로 단어로 분할되지 않도록(파일 이름 글로빙도 포함) 큰따옴표로 묶어야 합니다 $@.${options[@]}

답변3

출력이 안정적이고 양호한 쉘 구문 인 경우 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출력을 사용하여 명령 문자열을 안정적으로 만드는 것이 불가능합니다 .

관련 정보