다음에서: 쉘 명령 대체의 예기치 않은 동작
많은 수의 인수를 허용하는 명령이 있습니다. 그 중 일부는 합법적으로 공백(및 아마도 다른 것)을 포함할 수 있습니다.
따옴표를 사용하여 이러한 매개변수를 생성하는 스크립트를 작성했지만 출력을 복사하여 붙여넣어야 합니다.
./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
. 내부 명령은 실제로 쉘 스크립트 래퍼입니다).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
True입니다 .
에서 옵션을 저장 /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
출력을 사용하여 명령 문자열을 안정적으로 만드는 것이 불가능합니다 .