Le programme Bash n'est pas exécuté si la redirection échoue

9

En bash, je remarque que si une commande utilisant la redirection échoue, tous les programmes qui s'exécutent avant cela ne sont pas exécutés.

Par exemple, ce programme ouvre le fichier "a" et écrit 50 octets dans le fichier "a". Cependant, l'exécution de cette commande avec redirection vers un fichier avec des autorisations insuffisantes (~ root / log), ne produit aucun changement dans la taille du fichier 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

On pourrait penser que le programme fonctionnerait, capturer n'importe quelle sortie (mais aussi écrire dans le fichier "a"), puis échouer à écrire n'importe quelle sortie dans ~ root / log. Au lieu de cela, le programme n'est jamais exécuté.

Pourquoi cela et comment bash choisit-il l'ordre des "vérifications" qu'il effectue avant d'exécuter un programme? D'autres contrôles sont-ils également effectués?

ps J'essaie de déterminer si un programme exécuté sous cron s'est réellement exécuté lorsqu'il a été redirigé vers un fichier "permission refusée".

Charlie Dalsass
la source
Tout est en bon état de fonctionnement (c'est-à-dire les propriétaires et les autorisations de votre fichier .py), votre programme s'exécute correctement. Vos problèmes proviennent de la redirection. Vous n'êtes pas autorisé à écrire un répertoire filein / root. Et vous avez redirigé votre stdoutpour faire exactement cela. Ainsi, vous ne verrez aucune sortie, même si votre programme s'est exécuté.
MelBurslan
2
Mel, ce n'est pas vrai, le programme n'a jamais vraiment fonctionné. Voir les réponses ci-dessous.
Charlie Dalsass
Vous: "Exécutez le write_file.pyprogramme et envoyez sa sortie à ~root/logbash:" Désolé, mais vous n'êtes pas autorisé à écrire dans ce fichier! "Le shell fait exactement ce qu'il doit faire. S'il ne peut pas faire ce que vous lui avez demandé de faire faire, il vous informe immédiatement pourquoi il y a un problème, vous donnant la possibilité de décider comment y faire face. Pour tous les responsables de bash savent, de très mauvaises choses peuvent se produire si vous exécutez cette commande et ne parvenez pas à enregistrer la sortie. Si c'était assez important que vous ayez désigné un endroit pour le sauvegarder, ce serait une erreur pour ASS | U | ME que c'était OK de courir sans enregistrer stdout.
Monty Harder

Réponses:

18

Il ne s'agit pas vraiment de commander des chèques, simplement l'ordre dans lequel le shell met les choses en place. Les redirections sont définies avant l'exécution de la commande; dans votre exemple, le shell essaie de s'ouvrir ~root/logpour l'ajout avant d'essayer de faire quoi que ce soit impliquant ./write_file.py. Étant donné que le fichier journal ne peut pas être ouvert, la redirection échoue et le shell cesse de traiter la ligne de commande à ce stade.

Une façon de le démontrer est de prendre un fichier non exécutable et de tenter de l'exécuter:

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

Cela montre que le shell ne regarde même pas ./demolorsque la redirection ne peut pas être configurée.

Stephen Kitt
la source
Wow, c'est aussi simple que ça? Je ne savais pas que les redirections étaient effectuées en premier. Merci pour cette réponse et pour d'autres bonnes réponses également.
Charlie Dalsass
6
S'ils n'étaient pas effectués en premier, où la sortie serait-elle écrite?
Charles Duffy
Et si la sortie ne peut pas être écrite, comment savons-nous qu'il est sûr d'exécuter la commande? Peut-être que la commande génère des informations qui sont supprimées d'un magasin de données, et il est absolument nécessaire que la sortie soit capturée. Heureusement que bash ne le laissera pas fonctionner tant que vous n'aurez pas corrigé ces autorisations, hein?
Monty Harder
11

Depuis la page de manuel de bash, section REDIRECTION (souligné par moi):

Avant qu'une commande ne soit exécutée, ses entrées et sorties peuvent être redirigées à l'aide d'une notation spéciale interprétée par le shell.

...

Un échec d'ouverture ou de création d'un fichier entraîne l'échec de la redirection.

Le shell essaie donc d'ouvrir le fichier cible pour stdout, ce qui échoue, et la commande n'est pas exécutée du tout.

Murphy
la source
Merci beaucoup. Je souhaite que la page de manuel soit un peu clarifiée par "... si la sortie ne peut pas être redirigée, le programme ne sera pas exécuté".
Charlie Dalsass
Mise à jour; c'est plutôt caché quelques paragraphes ci-dessous.
Murphy
En fait, c'est assez clair. "Un échec d'ouverture ou de création d'un fichier entraîne l'échec de la redirection." Le voilà. Merci encore.
Charlie Dalsass
3

Il convient de noter que le shell doit établir des redirections avant de démarrer le programme.

Considérez votre exemple:

./write_file.py >> ~root/log

Ce qui se passe dans le shell est:

  1. Nous (la coquille) fork(); le processus enfant hérite des descripteurs de fichiers ouverts de son parent (le shell).
  2. Dans le processus enfant, nous fopen()(l'expansion de) "~ root / log", et dup2()cela à fd 1 (et close()le fd temporaire). En cas d' fopen()échec, appelez exit()pour signaler l'erreur au parent.
  3. Toujours chez l'enfant, nous exec()"./write_file.py". Ce processus n'exécute plus aucun de nos codes (sauf si nous n'avons pas réussi à l'exécuter, auquel cas nous devons exit()signaler l'erreur au parent).
  4. Le parent demandera wait()à l'enfant de se terminer et de gérer son code de sortie (en le copiant $?, au moins).

Ainsi, la redirection doit se produire dans l'enfant entre fork()et exec(): elle ne peut pas se produire avant fork()car elle ne doit pas changer la sortie standard du shell, et elle ne peut pas se produire après exec()parce que le nom de fichier et le code exécutable du shell ont maintenant été remplacés par le programme Python . Le parent n'a pas accès aux descripteurs de fichiers de l'enfant (et même si c'était le cas, il ne pouvait garantir la redirection entre exec()et la première écriture vers stdout).

Toby Speight
la source
0

Je suis désolé de vous informer que c'est tout le contraire. Le shell doit d'abord ouvrir ses E / S, puis passe le contrôle au programme.

tee pourrait s'avérer utile dans ce cas: ./write_file.py | tee -a ~root/log > /dev/null

Julie Pelletier
la source
Le script Python ne mourra-t-il pas simplement sur SIGPIPE après l'échec du tee?
Kevin
Pas selon le test que j'ai fait mais je vous suggère de l'essayer.
Julie Pelletier