Eu escrevi um "script embutido" que usa parâmetros posicionais, por exemplo
$ 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
Na verdade, quero executar meu script embutido usando o sudo --login
. Mas então os parâmetros posicionais não funcionam. Existe uma maneira de fazê-los funcionar?
(Também estou interessado se o comportamento está documentado ou padronizado em algum lugar).
$ 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
Versões de software:
$ 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
Pela mesma razão
Não gera
$HOME
, mas o conteúdo da$HOME
variável (do usuário de destino, conforme definido porsudo
).No seu caso, não é porque sh -c não expande os parâmetros posicionais , mas porque os
$0
,$1
,$2
já foram expandidos (pelo shell de login) no momento em quesh
foi iniciado.O que é mais surpreendente aqui é por que você vê
all=1 2
. E é a mesma razão pela qual você$@
vêsudo --login echo '$@'
.Ambos
-s
/--shell
e-i
/--login
executam um shell para executar o comando. Massudo
faz isso de uma maneira muito estranha.Eu esperaria
sudo -s 'some shell code'
executar um shell para interpretarsome shell code
, mas não é isso que ele faz. Com-s
, ele ainda espera executar um único comando simples e tenta citar (com\
) alguns caracteres na esperança de fazer isso acontecer por meio desse shell.Você não está destinado a fazer
sudo -s 'echo test'
, massudo -s echo test
. No primeiro caso, sudo realmente passaecho\ test
como código shell. Em shells do tipo Bourne, isso tenta executar um comando chamado'echo test'
. Com umrc
shell, isso executaria um comando chamadoecho\
comotest
argumento.Além do SPC, existem alguns caracteres que
sudo
escapam. Isso inclui backtick,;
,|
,(
,)
,*
,&
,=
,\
mas estranhamente, não$
(ou talvez essa seja a única razão de ser dessas-s
/--login
opções: ter um shell expandir variáveis).Ele inclui
@
embora. Se você observar o código , ele escapará de todos os bytes, exceto os alnums ASCII_
,-
e$
.Então:
sudo
realmente é executado"$SHELL" -c 'echo $\@'
e isso explica por que, no seu exemplo,$@
não é expandido pelo shell iniciado,sudo --login
mas por aquele que você diz para ele ser executado.Na tua
sudo
executaroot
o shell de login do '(bash
no seu caso) e diz para ele interpretar:Os argumentos foram unidos com espaços e todos os caracteres SPC,
"
,=
,&
,@
mas não$
foram escapados. Como aqueles$
não foram escapados, o shell de login do bash expandirá$0
,$1
,$2
mas não$\@
por causa dessa barra invertida.Você pode ver com:
Para que o shell de login
bash
acabe sendo executadosh
com-c
o primeiro argumento,como segundo argumento e
sh
,1
,2
como 3º, 4º e 5º.Para contornar isso, você pode usar
${0}
em vez de$0
, porquesudo
o transforma para$\{0\}
impedir que o shell de login o expanda para o conteúdo de seu$0
:Edit: a história traz luz à razão por trás disso
mais descobertas ao olhar para o código. A não fuga de
$
foi aparentemente introduzida em 2013 por esta mudança . Que se refere ao bug#564 que se refere ao bug#413 .Parece que antes que o bug#413 fosse "corrigido" ,
sudo
se comportou como eu esperava.Aquilo é:
O shell de login interpretou esse código de shell (e
sudo -s 'some shell code'
interpretou$SHELL
esse código de shell). Mas a resolução do bug#413 quebrou, porque a pessoa que relatou o bug não entendeu que funcionava dessa maneira. E o bug#564 quebrou ainda mais (em uma tentativa de reverter apenas parte da quebra introduzida pela resolução do bug#413).Cheguei a compilar
sudo
1.7.1 de 2009 e as opções-s
/-i
estavam funcionando como eu esperava na época.1.7.3
(com resolução do bug # 413) funcionou como você aparentemente esperava:Esse escape introduzido na correção para esse bug#413 é facilmente enganado porque bater a
\
na frente de todos os bytes (não caractere) não funciona para todos.Além do caso óbvio de
rc
onde\
nem é um operador de citação, na maioria dos shells, a nova linha não pode ser citada com\
:Isso também significa que argumentos vazios são descartados:
Além disso, ao inserir um
\
antes de cada byte , está transformando caracteres em outros em charsets onde o byte 0x5c (a codificação de\
) é encontrado em outros caracteres:0xa3 0x60 é grego Epsilon nessa localidade. E sudo muda o 0x60s para 0x5c 0x60 e 0xa3 0x5c é o alfabeto grego, o que explica por que o
uname
comando foi executado.sudo
mudou opara
E é claro que o comportamento acaba sendo surpreendente, pois os parâmetros posicionais
$-
, , e (onde é um nome de variável POSIX válido) são expandidos, mas não os outros parâmetros (como aqui, mas também , , , ) ou ou ( / / ) ou ( // ...).$$
$varname
varname
$@
$!
$?
$*
$#
${varname}
$var[1]
csh
tcsh
zsh
$var(1)
rc
es