Ao usar sudo -s
(abreviação da opção "--shell" ), é possível passar um comando "sudo", nesse caso ele executará o comando em um shell iniciado por "sudo" como usuário de destino.
(Da mesma forma, sudo -i
, também disponível como a opção "--login" , também inicia um shell e da mesma forma aceita um comando, que se comporta da mesma maneira.)
Executar comandos no sudo por meio de um shell pode ser importante em muitos casos:
- Ao usar curingas em um diretório ao qual o usuário atual não tem acesso, mas o root tem, o comando precisa ser executado em um shell de root para que os curingas sejam expandidos adequadamente.
- Executando um pipeline inteiro, muitos comandos encadeados em pipes (
|
). - Ao executar um shell embutido, como
for
,if
, etc. Executar um pequeno "script" inline inteiro em um únicosudo
pode ser útil.
A documentação da opção "-s" diz (ênfase minha):
Execute o shell especificado pela
SHELL
variável de ambiente se estiver definido ou o shell especificado pela entrada do banco de dados de senha do usuário que está chamando. Se um comando for especificado, ele será passado para o shell para execução por meio da opção -c do shell . Se nenhum comando for especificado, um shell interativo será executado. Observe que a maioria dos shells se comporta de maneira diferente quando um comando é especificado em comparação com uma sessão interativa; consulte o manual do shell para detalhes.
Em outras palavras, ao passar sudo -s
um comando, ele é passado para o shell usando a -c
opção, que pega uma string com um "script" e depois passa a executá-lo como um script de shell.
A documentação não vai muito além sobre como usar esta opção, ou apresentar exemplos, exceto dizer "consulte o manual do shell para detalhes". Isso implica que o comando recebido é passado diretamente para a opção do shell -c
. No entanto, como se vê, esse não é o caso.
Passar um script de shell com várias palavras falha:
$ sudo -s 'ls -ld /var/empty'
/bin/bash: ls -ld /var/empty: No such file or directory
A mensagem de erro indica que está tentando executar a string inteira como um comando simples... Hmmm, ok, então talvez adicionar espaços funcione? Sim, é o caso:
$ sudo -s ls -ld /var/empty
drwxr-xr-x. 3 root root 18 Jul 12 21:48 /var/empty
Mas não é bem assim que o shell -c
funciona... Bem, vamos tentar usar alguns metacaracteres, como ~
, que é um atalho para o diretório home, para ver como isso se comporta. Observe as ~
necessidades a serem citadas, para evitar que o shell não sudo o expanda (nesse caso, ele expandiria para a casa do usuário não root, em vez do /root
que é esperado):
$ sudo -s ls '~'
ls: cannot access '~': No such file or directory
Ok, então isso não funciona, e a saída de erro parece implicar que a expansão não está acontecendo, pois está preservando um literal ~
lá.
E os curingas? Também não funciona:
$ sudo -s ls '/root/*.cfg'
ls: cannot access '/root/*.cfg': No such file or directory
Em ambos os casos, executar o comando com $SHELL -c
funciona bem. Nesse caso, $SHELL
é bash, então:
$ sudo bash -c 'ls ~'
anaconda-ks.cfg
$ sudo bash -c 'ls /root/*.cfg'
/root/anaconda-ks.cfg
Uma exceção é que as variáveis parecem funcionar em sudo -s
, como:
$ sudo -s echo '$HOME'
/root
Então:
- O que está acontecendo aqui?
- Por que curingas e metacaracteres como
~
não funcionam no comando passado parasudo -s
ousudo -i
? - Dado que
$SHELL -c
recebe uma única string com um script, massudo -s
recebe vários argumentos, como o script é montado a partir dos argumentos? - Qual é uma maneira confiável de executar comandos em um shell via
sudo
?