Ooooh, isso é divertido .
A resposta curta: acontece lá porque o apt (ou algo que se bifurca) está lendo stdin nesse ponto em sua execução, e lê as linhas restantes do script porque é isso que ainda está em stdin naquele ponto. A pequena correção: coloque </dev/null
no final da linha apt-get install
e continue com o seu dia.
A resposta longa (sério, isso é um biggun): não há nada de especial sobre stdin / stdout / stderr, do ponto de vista de um processo em execução. Eles são apenas descritores de arquivos, e os descritores de arquivos são compartilhados entre processos quando são bifurcados. Então, o que acontece (mais ou menos) é:
-
A cópia do bash executando interativamente no seu terminal abre um novo
pipe
(2), em seguida, bifurca um novo processo, que fecha o stdout existente e, em seguida, torna o descritor do arquivo stdout (1) o final do gravador do tubo (vejadup2
(2)). Esse processo filho, em seguida,exec
scat sample.sh
, que lê o arquivo e o grava no que ele considera é stdout (mas é realmente o finalizador de um pipe). -
A cópia do bash executando interativamente no seu terminal bifurca outro novo processo, desta vez fechando o stdin existente, e então faz com que o descritor do arquivo stdin (0) seja o final do mesmo canal discutido anteriormente (novamente, com uma chamada para
dup2
). Este processo entãoexec
s seu processolxc-attach
.Se nada interferir com stdin ao longo do caminho (o que não acontece, neste caso particular) então todo processo que é bifurcado daquele que obteve o final do canal como stdin também tem exatamente o mesmo descritor de arquivo, anexado ao mesmo pipe, que tinha o conteúdo de
sample.sh
, como stdin. Qualquer Qualquer processo que lê a partir desse descritor de arquivo agora terá consumido os bytes lidos, e nenhum outro processo que lê a partir desse descritor de arquivo irá obter esses bytes específicos. Tome nota cuidadosa disso; você vai ver esse material novamente. -
Quando a festança no final de sua extravagância no encanamento de estilo armário de água italiano finalmente começa, vai ler "alguns" dos dados do tubo que é o seu stdin (porque é o que faz bash, quando chamado sem argumentos e sem um tty como stdin). Através da mágica de
strace
, eu apenas confirmei que bash realmente leu sua entrada um caractere de cada vez (ao invés de lê-lo em, digamos, 4k pedaços), então cada caractere individual que não faz parte de um comando que o bash tem, ou está atualmente executando, ainda estará sentado no pipe-which-bash-has-as-its-stdin. -
Quando o bash executa o segundo comando no seu script,
apt-get install
la la la, ele bifurca um novo processo. Que herda todos os descritores de arquivo do bash, incluindo (o mais importante) nosso bom amigo, o pipe-que-é-stdin . Isto também acontece para qualquer processo queapt-get
forks (isto é, deixe-me assegurar-lhe bastante). Um deles, ouapt-get
, está decidindo ler stdin e escrever o que lê em stdout (ou possivelmente stderr). -
Quando o
apt-get install
termina, o bash descobre o que a próxima coisa a executar é lendo o stdin mais uma vez. Porque algo else já leu tudo do canal, no entanto, não resta nada, e o bash interpreta "oh bem, acho que terminei então" e sai. Novamente, o pipe está vazio porque algo já o leu a seco, e tudo o que compartilha um único descritor de arquivo compartilha a recompensa dele.
A solução para o problema do "stdin compartilhado" é, sem surpresa, parar de passar por aí como um bong em uma festa de fraternidade. Já que você não pode parar fork
(2) de automaticamente dar a todos os mesmos descritores de arquivos, você precisa dizer ao bash, em vez disso, dar apt-get
(e qualquer outra coisa tomando um gole ilícito daquele doce) else para ativar, em vez disso. A coisa mais fácil de dar é /dev/null
- aquela fonte sempre fiel, nunca completa, de todo o seu "Dave não está aqui, cara" delicia. Esse é o domínio do "redirecionamento de entrada", que é o que o </dev/null
faz - ele diz: "hey bash, antes de você exec
that apt-get
, trocar stdin (descritor de arquivo 0) pelo descritor de arquivo obter da abertura /dev/null
".
Um exercício para o leitor terminar: tente colocar </dev/zero
após o comando apt-get install
e explique por que o que acontece acontece.