Programa Bash não executado se o redirecionamento falhar

9

No bash, noto que, se um comando usando redirecionamento falhar, quaisquer programas que sejam 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 esse comando com o redirecionamento para um arquivo com permissões insuficientes (~ root / log) não produz nenhuma alteração no tamanho do arquivo de "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, em seguida, falhar em gravar qualquer saída para ~ root / log. Em vez disso, o programa nunca é executado.

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

p.s. Eu estou tentando determinar se um programa executado sob o cron realmente correu quando redirecionado para um arquivo de "permissão negada".

    
por Charlie Dalsass 27.04.2016 / 15:43

4 respostas

18

Não é realmente uma questão de pedir cheques, simplesmente a ordem em que o shell configura as coisas. Redirecionamentos são configurados antes do comando ser executado; Portanto, no seu exemplo, o shell tenta abrir ~root/log para 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 sequer olha para ./demo quando o redirecionamento não pode ser configurado.

    
por 27.04.2016 / 15:47
11

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

Before a command is executed, its input and output may be redirected using a special notation interpreted by the shell.

...

A failure to open or create a file causes the redirection to fail.

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

    
por 27.04.2016 / 15:49
3

Vale a pena 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 (o shell) fork() ; o processo filho herda os descritores de arquivos abertos de seu pai (o shell).
  2. No processo filho, fopen() (a expansão de) "~ root / log" e dup2() it para fd 1 (e close() o fd temporário). Se o fopen() falhar, chame exit() para relatar o erro ao pai.
  3. Ainda no filho, nós exec() "./write_file.py". Esse processo não está mais executando nenhum código (a menos que tenhamos falhado a execução, caso em que exit() reportará o erro ao pai).
  4. O pai irá wait() para o filho encerrar e manipular seu código de saída (copiando-o para $? , pelo menos).

Portanto, o redirecionamento tem que acontecer no filho entre fork() e exec() : isso não pode acontecer antes de fork() porque não deve alterar o stdout do shell, e isso não pode acontecer depois de 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 se o fizesse, não poderia garantir o redirecionamento entre exec() e a primeira gravação para stdout).

    
por 28.04.2016 / 11:21
0

Lamento informar que é exatamente o oposto. 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

    
por 27.04.2016 / 15:50