나는 다음과 같은 위치 매개변수를 사용하는 "인라인 스크립트"를 작성했습니다.
$ 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
$2
sh
여기서 더 놀라운 점은 당신이 그것을 보는 이유 입니다 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
sudo
root
로그인 쉘( 귀하의 경우)을 실행 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
, 세 번째, 네 번째, 다섯 번째 매개변수 1
로2
${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년부터 sudo
1.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에 대한 수정 사항에 도입된 이스케이프는 모든 바이트(문자가 아닌) 앞에서 를 누르는 것이 모든 바이트에 적용되지 않기 때문에 속이기 쉽습니다 .
rc
where 가 인용 연산자가 아닌 명백한 경우를 제외하면 \
대부분의 쉘에서는 개행 문자를 인용할 수 없습니다 \
.
$ 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]
csh
tcsh
zsh
$var(1)
rc
es