Warum wird $ ls > ls.out
"ls.out" in die Liste der Dateinamen im aktuellen Verzeichnis aufgenommen? Warum wurde dies so gewählt? Warum nicht anders
command-line
bash
redirect
Edward Torvalds
quelle
quelle
ls > ../ls.out
Antworten:
Bei der Auswertung des Befehls wird zuerst die
>
Umleitung aufgelöst: Zum Zeitpunkt derls
Ausführung wurde die Ausgabedatei bereits erstellt.Dies ist auch der Grund, warum das Lesen und Schreiben in dieselbe Datei unter Verwendung einer
>
Umleitung innerhalb desselben Befehls die Datei abschneidet. Zum Zeitpunkt der Befehlsausführung wurde die Datei bereits abgeschnitten:Tricks, um dies zu vermeiden:
<<<"$(ls)" > ls.out
(Funktioniert für alle Befehle, die ausgeführt werden müssen, bevor die Umleitung aufgelöst wird.)Die Befehlsersetzung wird ausgeführt, bevor der äußere Befehl ausgewertet wird. Sie wird daher
ls
ausgeführt, bevor erls.out
erstellt wird:ls | sponge ls.out
(Funktioniert für alle Befehle, die ausgeführt werden müssen, bevor die Umleitung aufgelöst wird.)sponge
schreibt nur in die Datei, wenn der Rest der Pipe fertig ausgeführt wurde, wird alsols
ausgeführt, bevor siels.out
erstellt wird (sponge
wird mit demmoreutils
Paket geliefert ):ls * > ls.out
(funktioniert fürls > ls.out
den speziellen Fall)Die Dateinamenerweiterung wird ausgeführt, bevor die Umleitung aufgelöst wird. Sie wird daher
ls
mit den Argumenten ausgeführt, die Folgendes nicht enthaltenls.out
:Warum Umleitungen aufgelöst werden, bevor das Programm / Skript / was auch immer ausgeführt wird, ich sehe keinen bestimmten Grund, warum dies obligatorisch ist , aber ich sehe zwei Gründe, warum es besser ist , dies zu tun:
Wird STDIN nicht vorher umgeleitet, wird das Programm / Skript / was auch immer angehalten, bis STDIN umgeleitet wird.
Wenn Sie STDOUT nicht vorher umleiten, sollte die Shell die Ausgabe des Programms / Skripts / was auch immer zwischenspeichern, bis STDOUT umgeleitet wird.
Also Zeitverschwendung im ersten Fall und Zeit- und Gedächtnisverschwendung im zweiten Fall.
Dies ist genau das, was mir einfällt. Ich behaupte nicht, dass dies die eigentlichen Gründe sind. aber ich schätze, alles in allem, wenn man die Wahl hätte, würden sie aus den oben genannten Gründen sowieso vorher umleiten.
quelle
Von
man bash
:Der erste Satz legt nahe, dass die Ausgabe an einen anderen Ort als
stdin
mit Umleitung gesendet wird, bevor der Befehl ausgeführt wird. Um in eine Datei umgeleitet zu werden, muss die Datei zuerst von der Shell selbst erstellt werden.Um zu vermeiden, dass eine Datei vorhanden ist, sollten Sie die Ausgabe zuerst in die Named Pipe und dann in die Datei umleiten. Beachten Sie die Verwendung von
&
, um die Kontrolle über das Terminal an den Benutzer zurückzugebenAber wieso?
Denken Sie darüber nach - wo wird die Ausgabe sein? Ein Programm hat Funktionen wie
printf
,sprintf
,puts
, die alle standardmäßig unterwegsstdout
, kann aber ihre Ausgabe in Datei verschwunden sein , wenn die Datei in erster Linie gar nicht existiert? Es ist wie mit Wasser. Kannst du ein Glas Wasser bekommen, ohne zuerst Glas unter den Wasserhahn zu stellen?quelle
Ich bin mit den aktuellen Antworten nicht einverstanden. Die Ausgabedatei muss geöffnet werden, bevor der Befehl ausgeführt wird, oder der Befehl muss nirgendwo seine Ausgabe schreiben.
Dies liegt daran, dass "alles eine Datei ist" in unserer Welt. Die Ausgabe auf dem Bildschirm erfolgt über SDOUT (auch Dateideskriptor 1 genannt). Damit eine Anwendung auf das Terminal schreiben kann, wird fd1 geöffnet und wie eine Datei darauf geschrieben.
Wenn Sie die Ausgabe einer Anwendung in einer Shell umleiten, ändern Sie fd1 so, dass es tatsächlich auf die Datei zeigt. Wenn Sie eine Pipe erstellen, ändern Sie das STDOUT einer Anwendung in das STDIN einer anderen Anwendung (fd0).
Aber es ist alles schön, das zu sagen, aber man kann ziemlich leicht sehen, wie das funktioniert
strace
. Es ist ziemlich schweres Zeug, aber dieses Beispiel ist ziemlich kurz.Innerhalb sehen
strace.out
wir folgende Highlights:Dies öffnet sich
ls.out
alsfd3
. Schreibe nur Schneidet ab (überschreibt), falls vorhanden, andernfalls wird erstellt.Das ist ein bisschen Jonglieren. Wir schalten STDOUT (fd1) auf fd10 um und schließen es. Dies liegt daran, dass wir mit diesem Befehl nichts an das echte STDOUT ausgeben. Zum
ls.out
Abschluss wird der Schreibpunkt dupliziert und der ursprüngliche Punkt geschlossen.Dies ist die Suche nach der ausführbaren Datei. Eine Lektion vielleicht, um keinen langen Weg zu haben;)
Dann wird der Befehl ausgeführt und das übergeordnete Element wartet. Während dieses Vorgangs wird jedes STDOUT tatsächlich dem geöffneten Datei-Handle zugeordnet
ls.out
. Wenn das Kind Probleme hatSIGCHLD
, teilt dies dem übergeordneten Prozess mit, dass dieser beendet ist und wieder aufgenommen werden kann. Es endet mit ein wenig mehr Jonglieren und einem Abschluss vonls.out
.Warum wird so viel jongliert? Nein, ich bin mir auch nicht ganz sicher.
Natürlich können Sie dieses Verhalten ändern. Sie könnten mit so etwas in den Speicher puffern
sponge
und das wird vom Fortfahren-Befehl unsichtbar sein. Wir haben immer noch Auswirkungen auf die Dateideskriptoren, jedoch nicht auf eine für das Dateisystem sichtbare Weise.quelle
Es gibt auch einen schönen Artikel über die Implementierung von Umleitungs- und Pipe-Operatoren in der Shell . Was zeigt, wie eine Umleitung implementiert werden
$ ls > ls.out
könnte, könnte so aussehen:quelle