"sudo --login"에서 "sh -c"를 실행하면 위치 매개변수가 확장되지 않습니다. 해결책이 있나요?

"sudo --login"에서 "sh -c"를 실행하면 위치 매개변수가 확장되지 않습니다. 해결책이 있나요?

나는 다음과 같은 위치 매개변수를 사용하는 "인라인 스크립트"를 작성했습니다.

$ sudo sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2

실제로 run my script inline 을 사용하고 싶습니다 sudo --login. 그러나 위치 매개변수는 아무런 영향을 미치지 않습니다. 작동하게 하는 방법이 있나요?

(이 동작이 문서화되었거나 어딘가에 표준화되어 있는지도 궁금합니다.)

$ sudo --login sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=-bash
p1=
p2=
all=1 2

소프트웨어 버전:

$ sh --version
GNU bash, version 5.0.7(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

$ sudo --version
Sudo version 1.8.27
Sudoers policy plugin version 1.8.27
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.27

답변1

같은 이유로

sudo --login echo '$HOME'

을 출력하는 대신 $HOME변수 $HOME(대상 사용자가 지정한 sudo)의 내용을 출력합니다.

귀하의 경우에는 그렇지 않습니다.sh -c는 위치 매개변수를 확장하지 않습니다., 그러나 시작 시 (로그인 쉘을 통해) 확장되었기 때문 $0입니다 .$1$2sh

여기서 더 놀라운 점은 당신이 그것을 보는 이유 입니다 all=1 2.$@sudo --login echo '$@'

-s/ --shell-i/ --login실행 쉘은 모두 명령을 실행합니다. 그런데 sudo아주 이상한 방식으로요.

sudo -s 'some shell code'설명하기 위해 쉘을 실행 하고 싶었지만 some shell code그게 목적이 아닙니다. 이를 위해 -s여전히 간단한 명령을 실행하고 \쉘이 이를 수행할 것이라는 희망으로 일부 문자를 인용하려고 시도합니다.

sudo -s 'echo test'그러나 이렇게 해서는 안 됩니다 sudo -s echo test. 첫 번째 경우 sudo는 실제로 echo\ test쉘 코드로 전달됩니다. Bourne과 유사한 쉘에서는 이름이 지정된 명령을 실행하려고 시도합니다 'echo test'. 쉘을 사용하면 인수를 인수로 사용하여 명령 rc을 실행합니다 .echo\test

SPC 외에도 일부 sudo이스케이프 문자가 있습니다. 여기에는 눈에 띄지 않는 숫자 ,,,,,,,,,,,,가 포함되지만 ;이상하게도 그렇지 않습니다(또는 이것이 이러한/옵션의 유일한 이유일 수도 있습니다: 쉘 확장 변수를 허용합니다).|()*&=\$-s--login

그러나 @. 당신이 보면암호, ASCII 산술을 제외한 모든 바이트를 이스케이프 처리 _하고 .-$

 if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
   *dst++ = '\\';

그래서:

sudo -s echo '$@'

sudo실제로 실행됩니다 "$SHELL" -c 'echo $\@'. 이는 귀하의 예에서 $@시작된 쉘이 확장되는 것이 아니라 sudo --login 실행하도록 지시하는 쉘인 이유를 설명합니다.

당신의

sudo sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2

sudoroot로그인 쉘( 귀하의 경우)을 실행 bash하고 다음을 해석하도록 지시하십시오.

sh -c echo\ \"p0\=$0\"\ \&\&\ echo\ \"p1\=$1\"\ \&\&\ echo\ \"p2\=$2\"\ \&\&\ echo\ \"all\=$\@\" sh 1 2

매개변수는 공백 및 모든 SPC,,,,, 문자와 연결되지만 "이스케이프되지는 않습니다. 이는 이스케이프되지 않기 때문에 bash 로그인 쉘은 백슬래시 때문에 확장되지 않습니다.=&@$$$0$1$2$\@

다음을 통해 볼 수 있습니다:

$ SHELL=echo sudo -s sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
-c sh -c echo\ \"p0\=$0\"\ \&\&\ echo\ \"p1\=$1\"\ \&\&\ echo\ \"p2\=$2\"\ \&\&\ echo\ \"all\=$\@\" sh 1 2

이런 식으로 로그인 bash쉘은 결국 첫 번째 매개변수 sh로 실행됩니다.-c

echo "p0=-bash" && echo "p1=" && echo "p2=" && echo "all=$@"

두 번째 매개변수로 sh, 세 번째, 네 번째, 다섯 번째 매개변수 12

${0}이 문제를 해결하려면 대신 를 사용하면 됩니다 $0. sudo변환하면 $\{0\}로그인 셸이 콘텐츠로 확장하는 것을 방지할 수 있기 때문입니다.그것은 $0:

$ sudo --login sh -c 'echo "p0=${0}" && echo "p1=${1}" && echo "p2=${2}" && echo "all=${@}"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2

편집자: 역사는 그 이유를 드러냅니다.

코드를 살펴보면 더 많은 발견이 이루어질 것입니다. Unescapability는 $2013년에 도입된 것으로 보입니다.이번 변화를 통해. 추천버그 #564이는 다음을 가리킨다.버그 #413.

버그#413이 있었던 것 같습니다."안정적인", sudo예상대로 수행되었습니다.

그건:

sudo -i 'some shell code'

로그인 쉘이 쉘 코드를 해석하도록 합니다(그리고 쉘 코드가 해석 sudo -s 'some shell code'되도록 합니다 ). $SHELL하지만 버그#413 해결 방법은 버그를 보고한 사람이 버그가 어떻게 작동하는지 이해하지 못했기 때문에 문제를 해결했습니다. bug#564는 문제를 더욱 심화시킵니다(bug#413에 대한 솔루션으로 인해 발생한 피해의 일부만 취소하려고 시도함).

2009년부터 sudo1.7.1을 컴파일했는데 -s/ -i옵션이 예상대로 작동했습니다. 1.7.3(버그#413에 대한 솔루션)은 분명히 예상한 대로 작동합니다.

$ sudo-1.7.1 -i 'echo "$SHELL"'
/bin/bash
$ sudo-1.7.3 -i 'echo "$SHELL"'
-bash: echo "$SHELL": command not found
$ sudo-1.8.21p2 -i 'echo "$SHELL"'
-bash: echo "/bin/bash": No such file or directory
$ sudo-1.7.3 -i sh -c 'echo "p0=$0" && echo "p1=$1" && echo "p2=$2" && echo "all=$@"' sh 1 2
p0=sh
p1=1
p2=2
all=1 2

\버그#413에 대한 수정 사항에 도입된 이스케이프는 모든 바이트(문자가 아닌) 앞에서 를 누르는 것이 모든 바이트에 적용되지 않기 때문에 속이기 쉽습니다 .

rcwhere 가 인용 연산자가 아닌 명백한 경우를 제외하면 \대부분의 쉘에서는 개행 문자를 인용할 수 없습니다 \.

$ SHELL=sh sudo -s echo $'a\nb'
ab
$ SHELL=csh  sudo -s echo $'a\nb'
a b

이는 또한 빈 매개변수가 삭제됨을 의미합니다.

$ sudo printf '<%s>\n' a '' b
<a>
<>
<b>
$ sudo -s printf '<%s>\n' a '' b
<a>
<b>

\또한 각 항목 앞에 삽입하여바이트는 문자를 문자 세트의 다른 문자로 변환합니다. 여기서 \바이트 0x5c(의 인코딩)는 다른 문자 중에서 발견됩니다.

$ SHELL=bash LC_ALL=zh_HK.big5hkscs sudo -s echo $'\xa3``uname`\xa3`'
bash: line 2: Linuxα: command not found
α

0xa3 0x60은 이 로케일의 그리스어 엡실론입니다. sudo는 0x60s를 0x5c 0x60으로 변경하고 0xa3 0x5c는 uname명령이 실행되는 이유를 설명하는 그리스 문자입니다. sudo변경됨

ε`uname`ε

도착하다

\α`\`uname\`\α`

물론 , $-, $$위치 매개변수 및 $varname( varname유효한 POSIX 변수 이름이 있는 곳)은 확장되지만 다른 매개변수는 확장되지 않기 때문에(예 : $@여기에서도 $!, $?, $*) 또는 ( // ) 또는 (//...).$#${varname}$var[1]cshtcshzsh$var(1)rces

관련 정보