Programa Bash não executado se o redirecionamento falhar

9

No bash, percebo que, se um comando usando o redirecionamento falhar, todos os programas executados antes dele não serão executados.

Por exemplo, este programa abre o arquivo "a" e grava 50 bytes no arquivo "a". No entanto, executar este comando com o redirecionamento para um arquivo com permissões insuficientes (~ root / log), não produz alterações no tamanho do arquivo "a".

$ ./write_file.py >> ~root/log
-bash: /var/root/log: Permission denied
cdal at Mac in ~/experimental/unix_write
$ ls -lt
total 16
-rw-rw-r--  1 cdal  staff  0 Apr 27 08:54 a <-- SHOULD BE 50 BYTES

Alguém poderia pensar que o programa seria executado, capturar qualquer saída (mas também gravar no arquivo "a") e depois falhar em gravar qualquer saída no ~ root / log. Em vez disso, o programa nunca é executado.

Por que isso e como o bash escolhe a ordem das "verificações" antes de executar um programa? Também são realizadas outras verificações?

ps Estou tentando determinar se um programa executado no cron realmente foi executado quando redirecionado para um arquivo "permissão negada".

Charlie Dalsass
fonte
Tudo está em boas condições de funcionamento (isto é, propriedade e permissão do seu arquivo .py), mas o seu programa é executado com êxito. Seus problemas vêm do redirecionamento. Você não tem permissão para gravar um arquivo no diretório / raiz. E você redirecionou o seu stdoutpara fazer exatamente isso. Portanto, você não verá nenhuma saída, mesmo que seu programa tenha sido executado.
MelBurslan
2
Mel, isso não é verdade, o programa nunca foi executado. Veja as respostas abaixo.
precisa
Você: "Execute o write_file.pyprograma e envie sua saída para o ~root/logbash:" Desculpe, mas você não tem permissão para gravar nesse arquivo! "O shell está fazendo exatamente o que deve fazer. Se não puder fazer o que você pediu , informa imediatamente por que há um problema, dando a você a oportunidade de decidir como lidar com ele.Para todos os mantenedores do bash, coisas muito ruins podem acontecer se você executar esse comando e não conseguir salvar a saída. Se fosse o suficiente importante que você designado um lugar para guardá-lo, seria errado ASS | L | ME foi OK para executar sem salvar stdout.
Monty mais dura

Respostas:

18

Não se trata realmente de pedir cheques, simplesmente a ordem na qual o shell configura as coisas. Os redirecionamentos são configurados antes da execução do comando; portanto, no seu exemplo, o shell tenta abrir ~root/logpara anexar antes de tentar fazer algo que envolva ./write_file.py. Como o arquivo de log não pode ser aberto, o redirecionamento falha e o shell para de processar a linha de comando nesse ponto.

Uma maneira de demonstrar isso é pegar um arquivo não executável e tentar executá-lo:

$ touch demo
$ ./demo
zsh: permission denied: ./demo
$ ./demo > ~root/log
zsh: permission denied: /root/log

Isso mostra que o shell nem olha ./demoquando o redirecionamento não pode ser configurado.

Stephen Kitt
fonte
Uau, é simples assim? Não percebi que os redirecionamentos foram feitos primeiro. Obrigado por esta resposta e por outras ótimas respostas também.
precisa
6
Se eles não foram feitos primeiro, para onde a saída seria gravada?
26616 Charles Duffy
E se a saída não puder ser gravada, como sabemos que é seguro executar o comando? Talvez o comando produza informações que estão sendo excluídas de um armazenamento de dados e é absolutamente necessário que a saída seja capturada. Ainda bem que o bash não o deixará funcionar até que você corrija essas permissões, não é?
Monty Mais difícil
11

Na página do manual do bash, seção REDIRECTION (ênfase minha):

Antes de um comando ser executado, sua entrada e saída podem ser redirecionadas usando uma notação especial interpretada pelo shell.

...

Uma falha ao abrir ou criar um arquivo causa falha no redirecionamento.

Portanto, o shell tenta abrir o arquivo de destino stdout, o que falha, e o comando não é executado.

Murphy
fonte
Muito obrigado. Desejo que a página de manual seja esclarecida um pouco com "... se a saída não puder ser redirecionada, o programa não será executado".
precisa
Atualizada; está bastante escondido alguns parágrafos abaixo.
Murphy
Na verdade, é bem claro. "Uma falha ao abrir ou criar um arquivo causa falha no redirecionamento." Aí está. Obrigado novamente.
Charlie Dalsass
3

Vale observar que o shell deve estabelecer redirecionamentos antes de iniciar o programa.

Considere o seu exemplo:

./write_file.py >> ~root/log

O que acontece no shell é:

  1. Nós (a concha) fork(); o processo filho herda os descritores de arquivo aberto de seu pai (o shell).
  2. No processo filho, nós fopen()(a expansão de) "~ root / log", e dup2()para fd 1 (e close()o fd temporário). Se fopen()falhar, ligue exit()para relatar o erro ao pai.
  3. Ainda na criança, nós exec()"./write_file.py". Agora, esse processo não está mais executando nenhum código (a menos que tenhamos falhado em executar; nesse caso, exit()reportaremos o erro ao pai).
  4. O pai deseja wait()que o filho termine e manipule seu código de saída (copiando-o para $?, pelo menos).

Portanto, o redirecionamento deve ocorrer no filho entre fork()and exec(): ele não pode acontecer antes fork()porque não deve alterar o stdout do shell e depois não exec()porque o nome do arquivo e o código executável do shell foram substituídos pelo programa Python . O pai não tem acesso aos descritores de arquivo do filho (e, mesmo que tivesse, não poderia garantir o redirecionamento entre exec()e a primeira gravação no stdout).

Toby Speight
fonte
0

Lamento informar que é exatamente o contrário. O shell precisa abrir sua E / S primeiro e depois passar o controle para o programa.

tee pode ser útil neste caso: ./write_file.py | tee -a ~root/log > /dev/null

Julie Pelletier
fonte
O script Python não irá morrer no SIGPIPE depois que o tee falhar?
Kevin
Não de acordo com o teste que fiz, mas sugiro que tente.
Julie Pelletier