TL; DR: Ao tomar um comando nas opções "--shell" ou "--login", sudo irá escapar da maioria dos caracteres (usando escapes de barra invertida), incluindo todos os metacaracteres ( exceto por $ ), incluindo também espaços.
Isso quebra todos os casos de uso nos quais você deseja usar um shell, o que torna sudo -s inadequada para executar comandos do shell que precisam para serem executados como comandos do shell.
Em vez de sudo -s , use sudo sh -c '...' ou sudo bash -c '...' (ou seja qual for o% esperado$SHELL.) Em vez de sudo -i , use sudo bash -l -c '...' (assumindo que o shell do root seja bash novamente.)
Analisando a parte relevante do código fonte do sudo , há este snippet:
/* quote potential meta characters */
if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
*dst++ = '\';
*dst++ = *src;
O fragmento é executado sobre cada caractere de cada argumento. Se o caractere não for alfanumérico, sublinhado, traço ou cifrão, ele será salvo com uma barra invertida.
Isso significa que os caracteres curinga * , ? , [...] , etc. serão ignorados para \* , \? , \[...\] .
Também metacaracteres, como ~ , ; , | , etc., serão escapados para \~ , \; , \| .
Uma string com espaços ls /root será ignorada para ls\ /root .
Por algum motivo, $ é uma exceção no escape, é por isso que sudo -s echo '$HOME' funciona, pois o $ será mantido sem escape. (Observe que isso só funciona para variáveis, e nem mesmo para a forma ${HOME} , caso em que as chaves serão escapadas, o que quebrará a expressão.)
As conseqüências dessa citação é que a maioria dos casos que exigem executar um shell como root não pode se beneficiar do formato sudo -s <command> :
- Os wilcards não serão expandidos, pois terão escape e não funcionarão como curingas.
- Os pipelines não funcionarão, pois o canal
|será ignorado e também não funcionará. - Comandos como
foreifnormalmente precisam usar;, que será escapado e não funcionará também. Uma nova linha seria uma alternativa, mas também não parece haver uma maneira de introduzir uma nova linha sem escape.
Em suma, o formulário sudo -s <command> parece funcionar apenas para casos que não envolvem curingas, pipelines, scriptlets, etc. Mas, em seguida, você normalmente não precisa de um shell para executar esses comandos! Apenas executar sudo <command> sem o -s é normalmente suficiente para esses casos.
Não está claro qual a motivação para a implementação do sudo. Talvez para manter alguma consistência entre o tratamento de comandos ao adicionar ou remover o -s . Talvez também porque -i preceda -s e, nesse caso, há uma pequena diferença em como as variáveis de ambiente são definidas ...
SOLUÇÃO ALTERNATIVA: A solução é executar o -c explicitamente.
Isso envolve saber que $SHELL é para o usuário de destino (que pode não corresponder ao usuário atual, portanto, usar $SHELL diretamente nem sempre é correto.)
Por exemplo, em vez de sudo -s ls -l '/root/*.cfg' , use:
$ sudo bash -c 'ls -l /root/*.cfg'
E, em vez de sudo -i ls -l '~' , use:
$ sudo bash -l -c 'ls -l ~'
(O argumento -l do bash cria um shell de "login", que é equivalente ao criado por sudo -i .)