Eu escrevi um script que alterna usuários durante a execução e o executei usando o redirecionamento de arquivo para o padrão. Assim user-switch.sh
é ...
#!/bin/bash
whoami
sudo su -l root
whoami
E executá-lo bash
me dá o comportamento que eu espero
$ bash < user-switch.sh
vagrant
root
No entanto, se eu executar o script sh
, recebo uma saída diferente
$ sh < user-switch.sh
vagrant
vagrant
Por que está bash < user-switch.sh
dando resultados diferentes sh < user-switch.sh
?
Notas:
- acontece em duas caixas diferentes executando o Debian Jessie
bash
io-redirection
stdin
dash
popedotninja
fonte
fonte
sudo su
: unix.stackexchange.com/questions/218169/…/bin/sh
no (minha cópia do) Debian é Dash, sim. Eu nem sabia o que era até agora. Eu fiquei com o bash até agora.user-switch.sh
em um trabalho de Jenkins, e o script foi executado. A execução do mesmo script fora do Jenkins produziu resultados diferentes, e recorri ao redirecionamento de arquivos para obter o comportamento que vi no Jenkins.Respostas:
Um script semelhante, sem
sudo
, mas com resultados semelhantes:Com
bash
, o restante do script é usado como entrada parased
, comdash
, o shell o interpreta.Executando
strace
neles:dash
lê um bloco do script (oito kB aqui, mais do que suficiente para armazenar todo o script) e depois gerased
:O que significa que o identificador de arquivo está no final do arquivo e
sed
não verá nenhuma entrada. A parte restante está sendo armazenada em bufferdash
. (Se o script for maior que o tamanho do bloco de 8 kB, a parte restante será lida porsed
.)Bash, por outro lado, procura voltar ao final do último comando:
Se a entrada vier de um tubo, como aqui:
não é possível rebobinar, pois não é possível procurar tubos e soquetes. Nesse caso, o Bash volta a ler a entrada, um caractere de cada vez, para evitar a substituição. (
fd_to_buffered_stream()
ininput.c
) Fazer uma chamada de sistema completa para cada byte não é muito eficaz em princípio. Na prática, não acho que as leituras sejam uma grande sobrecarga em comparação, por exemplo, ao fato de que a maioria das coisas que o shell envolve gera processos totalmente novos.Uma situação semelhante é a seguinte:
O subshell deve garantir que
read
apenas leia a primeira nova linha, parahead
ver a próxima linha. (Isso funcionadash
também.)Em outras palavras, o Bash se esforça para dar suporte à leitura da mesma fonte para o próprio script e para comandos executados a partir dele.
dash
não. Ozsh
eksh93
empacotado em Debian ir com Bash sobre este assunto.fonte
strace
mais tarde e comparar as saídas. Eu não tinha pensado em usar essa abordagem.O shell está lendo o script da entrada padrão. Dentro do script, você executa um comando que também deseja ler a entrada padrão. Qual entrada vai para onde? Você não pode dizer com segurança .
A maneira como os shells funcionam é que eles leem um pedaço do código-fonte, o analisam e, se encontrarem um comando completo, executam o comando e prosseguem com o restante do pedaço e o restante do arquivo. Se o pedaço não contém um comando completo (com um caractere final no final - acho que todos os shells são lidos até o final de uma linha), o shell lê outro pedaço, e assim por diante.
Se um comando no script tentar ler o mesmo descritor de arquivo do qual o shell está lendo o script, o comando encontrará o que vier depois do último pedaço que ele leu. Esse local é imprevisível: depende do tamanho do bloco escolhido pelo shell, e isso pode depender não apenas do shell e de sua versão, mas também da configuração da máquina, memória disponível etc.
O Bash procura o final do código-fonte de um comando no script antes de executar o comando. Isso não é algo com o qual você pode contar, não apenas porque outros shells não o fazem, mas também porque isso só funciona se o shell estiver lendo um arquivo regular. Se o shell estiver lendo de um canal (por exemplo
ssh remote-host.example.com <local-script-file.sh
), os dados lidos serão lidos e não poderão ser lidos.Se você deseja passar a entrada para um comando no script, é necessário fazê-lo explicitamente, geralmente com um documento aqui . (Um documento aqui é geralmente o mais conveniente para a entrada de várias linhas, mas qualquer método serve.) O código que você escreveu funciona apenas em alguns shells, apenas se o script for passado como entrada para o shell a partir de um arquivo regular; se você esperava que o segundo
whoami
fosse passado como entrada parasudo …
, pense novamente, tendo em mente que na maioria das vezes o script não é passado para a entrada padrão do shell.Observe que nesta década, você pode usar
sudo -i root
. Corrersudo su
é um truque do passado.fonte