Bash-Programm wird nicht ausgeführt, wenn die Umleitung fehlschlagen würde

9

In Bash stelle ich fest, dass alle Programme, die zuvor ausgeführt wurden, nicht ausgeführt werden, wenn ein Befehl mit Umleitung fehlschlagen würde.

Beispielsweise öffnet dieses Programm die Datei "a" und schreibt 50 Bytes in die Datei "a". Wenn Sie diesen Befehl jedoch mit Umleitung zu einer Datei mit unzureichenden Berechtigungen (~ root / log) ausführen, ändert sich die Dateigröße von "a" nicht.

$ ./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

Man würde denken, das Programm würde laufen, jede Ausgabe erfassen (aber auch in die Datei "a" schreiben) und dann keine Ausgabe in ~ root / log schreiben. Stattdessen wird das Programm nie ausgeführt.

Warum ist das so und wie wählt bash die Reihenfolge der "Überprüfungen", die es vor der Ausführung eines Programms durchführt? Werden auch andere Prüfungen durchgeführt?

ps Ich versuche festzustellen, ob ein unter cron ausgeführtes Programm tatsächlich ausgeführt wurde, wenn es in eine Datei mit der Berechtigung "Berechtigung verweigert" umgeleitet wurde.

Charlie Dalsass
quelle
Alles funktioniert einwandfrei (dh Eigentümer und Berechtigungen Ihrer .py-Datei). Ihr Programm wird einwandfrei ausgeführt. Ihre Probleme ergeben sich aus der Weiterleitung. Sie haben keine Berechtigung zum Schreiben eines Datei- / Stammverzeichnisses. Und Sie haben Ihre umgeleitet stdout, um genau das zu tun. Sie sehen also keine Ausgabe, obwohl Ihr Programm ausgeführt wurde.
MelBurslan
2
Mel, das stimmt nicht, das Programm lief nie wirklich. Siehe Antworten unten.
Charlie Dalsass
Sie: "Führen Sie das write_file.pyProgramm aus und senden Sie seine Ausgabe an ~root/logbash:" Entschuldigung, aber Sie dürfen nicht in diese Datei schreiben! "Die Shell tut genau das, was sie tun soll. Wenn sie nicht das tun kann, worum Sie es gebeten haben Wenn Sie dies tun, werden Sie sofort darüber informiert, warum ein Problem vorliegt, und Sie können entscheiden, wie Sie damit umgehen möchten. Nach Ansicht aller Bash-Betreuer können sehr schlimme Dinge passieren, wenn Sie diesen Befehl ausführen und die Ausgabe nicht speichern können. Wenn es wichtig genug wäre, dass Sie einen Ort zum Speichern festgelegt haben, wäre es falsch, ASS | U | ME zu verwenden. Es war in Ordnung, ohne Speichern von stdout zu laufen.
Monty Harder

Antworten:

18

Es geht nicht wirklich darum, Schecks zu bestellen, sondern nur um die Reihenfolge, in der die Shell die Dinge einrichtet. Umleitungen werden eingerichtet, bevor der Befehl ausgeführt wird. In Ihrem Beispiel versucht die Shell, sie ~root/logzum Anhängen zu öffnen , bevor Sie versuchen, etwas zu tun, das sie betrifft ./write_file.py. Da die Protokolldatei nicht geöffnet werden kann, schlägt die Umleitung fehl und die Shell beendet die Verarbeitung der Befehlszeile an diesem Punkt.

Eine Möglichkeit, dies zu demonstrieren, besteht darin, eine nicht ausführbare Datei zu verwenden und zu versuchen, sie auszuführen:

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

Dies zeigt, dass die Shell nicht einmal sieht, ./demowann die Umleitung nicht eingerichtet werden kann.

Stephen Kitt
quelle
Wow, so einfach ist das? Mir war nicht klar, dass zuerst Umleitungen durchgeführt wurden. Vielen Dank für diese Antwort und für andere großartige Antworten.
Charlie Dalsass
6
Wenn sie nicht zuerst gemacht würden, wohin würde die Ausgabe geschrieben werden?
Charles Duffy
Und wenn die Ausgabe nicht geschrieben werden kann, woher wissen wir, dass es sicher ist, den Befehl auszuführen? Möglicherweise gibt der Befehl Informationen aus, die aus einem Datenspeicher gelöscht werden, und es ist unbedingt erforderlich, dass die Ausgabe erfasst wird. Gut, dass bash es nicht laufen lässt, bis Sie diese Berechtigungen behoben haben, oder?
Monty Harder
11

Auf der Bash-Manpage, Abschnitt REDIRECTION (Hervorhebung von mir):

Bevor ein Befehl ausgeführt wird, können seine Eingabe und Ausgabe unter Verwendung einer von der Shell interpretierten speziellen Notation umgeleitet werden.

...

Wenn eine Datei nicht geöffnet oder erstellt wird, schlägt die Umleitung fehl.

Die Shell versucht also, die Zieldatei für zu öffnen stdout, was fehlschlägt, und der Befehl wird überhaupt nicht ausgeführt.

Murphy
quelle
Vielen Dank. Ich wünschte, die Manpage würde ein wenig mit "... wenn die Ausgabe nicht umgeleitet werden kann, wird das Programm nicht ausgeführt" geklärt.
Charlie Dalsass
Aktualisiert; Es ist eher einige Absätze unten versteckt.
Murphy
Eigentlich ist es ziemlich klar. "Wenn eine Datei nicht geöffnet oder erstellt wird, schlägt die Umleitung fehl." Da ist es. Danke noch einmal.
Charlie Dalsass
3

Es ist zu beachten, dass die Shell vor dem Starten des Programms Umleitungen einrichten muss .

Betrachten Sie Ihr Beispiel:

./write_file.py >> ~root/log

Was in der Shell passiert, ist:

  1. Wir (die Muschel) fork(); Der untergeordnete Prozess erbt die offenen Dateideskriptoren von seinem übergeordneten Prozess (der Shell).
  2. Im untergeordneten Prozess haben wir fopen()(die Erweiterung von) "~ root / log" und dup2()es zu fd 1 (und close()dem temporären fd). Wenn dies fopen()fehlschlägt, rufen Sie exit()an, um den Fehler dem übergeordneten Element zu melden.
  3. Noch im Kind, wir exec()"./write_file.py". Dieser Prozess führt jetzt keinen unserer Codes mehr aus (es sei denn, wir haben ihn nicht ausgeführt. In diesem Fall müssen wir exit()den Fehler dem übergeordneten Code melden).
  4. Der Elternteil wird wait()das Kind beenden und seinen Exit-Code verarbeiten ( $?zumindest durch Kopieren in ).

Die Umleitung muss also im untergeordneten Element zwischen fork()und erfolgen exec(): Sie kann nicht vorher erfolgen, fork()da sie das Standardout der Shell nicht ändern darf, und sie kann nicht danach erfolgen, exec()da der Dateiname und der ausführbare Code der Shell jetzt durch das Python-Programm ersetzt wurden . Das übergeordnete Element hat keinen Zugriff auf die Dateideskriptoren des untergeordneten Elements (und selbst wenn dies der Fall ist, kann nicht garantiert werden, dass zwischen exec()und dem ersten Schreiben in stdout umgeleitet wird ).

Toby Speight
quelle
0

Es tut mir leid, Ihnen mitteilen zu müssen, dass das Gegenteil der Fall ist. Die Shell muss zuerst ihre E / A öffnen und dann die Steuerung an das Programm übergeben.

tee könnte sich in diesem Fall als hilfreich erweisen: ./write_file.py | tee -a ~root/log > /dev/null

Julie Pelletier
quelle
Wird das Python-Skript nicht einfach auf SIGPIPE sterben, nachdem der Abschlag fehlgeschlagen ist?
Kevin
Nicht nach dem Test, den ich gemacht habe, aber ich schlage vor, dass Sie es versuchen.
Julie Pelletier