Shell-Skripte bieten eine sehr mächtige Funktion:die Fähigkeit, die Ausgabe von Befehlen und Skripten umzuleiten und sie an Dateien, Geräte oder sogar als Eingabe für andere Befehle oder Skripte zu senden.
Dieser Artikel konzentriert sich auf die Befehls- und Skriptausgabe.
Arten der Ausgabe
Befehle und Skripte in einer Shell können zwei grundlegende Arten von Ausgaben erzeugen:
- STDOUT: Die normale Ausgabe eines Befehls/Skripts (Dateideskriptor 1)
- STDERR: Die Fehlerausgabe eines Befehls/Skripts (Dateideskriptor 2)
Standardmäßig werden STDOUT und STDERR an den Bildschirm Ihres Terminals gesendet.
In Bezug auf die Eingabe STDIN liest standardmäßig Eingaben von der Tastatur (Dateideskriptor 0). Ein Dateideskriptor ist ein eindeutiger Bezeichner für eine Datei oder eine andere I/O-Ressource.
Wie man die Shell-Ausgabe umleitet
Es gibt mehrere Möglichkeiten, die Ausgabe von Shell-Skripten und -Befehlen umzuleiten.
1. STDOUT umleiten
Für die folgenden Beispiele verwende ich diesen einfachen Satz von Dateien:
$ls -la file*
-rw-r--r--. 1 admin2 admin2 7 Mar 27 15:34 file1.txt
-rw-r--r--. 1 admin2 admin2 10 Mar 27 15:34 file2.txt
-rw-r--r--. 1 admin2 admin2 13 Mar 27 15:34 file3.txt
Ich verwende ein einfaches ls
Befehle, um STDOUT vs. STDERR zu veranschaulichen, aber das gleiche Prinzip gilt für die meisten Befehle, die Sie von einer Shell aus ausführen.
Ich kann die Standardausgabe mit ls file* > my_stdout.txt
in eine Datei umleiten :
$ls file* > my_stdout.txt
$ cat my_stdout.txt
file1.txt
file2.txt
file3.txt
Als nächstes führe ich einen ähnlichen Befehl aus, aber mit einem 1
vor >
. Umleitung mit >
Signal ist dasselbe wie die Verwendung von 1>
Um dies zu tun:Ich sage der Shell, dass sie die STDOUT in diese Datei umleiten soll. Wenn ich den Dateideskriptor weglasse, wird standardmäßig STDOUT verwendet. Ich kann dies beweisen, indem ich sdiff
ausführe Befehl, um die Ausgabe beider Befehle nebeneinander anzuzeigen:
$ls file* 1> my_other_stdout.txt
$sdiff my_stdout.txt my_other_stdout.txt
file1.txt file1.txt
file2.txt file2.txt
file3.txt file3.txt
Wie Sie sehen, haben beide Ausgaben denselben Inhalt.
2. STDERR umleiten
Was ist nun das Besondere an STDERR? Zur Veranschaulichung füge ich dem vorherigen Beispiel mit ls file* non-existing-file* > my_normal_output.txt
eine Fehlerbedingung hinzu :
Hier ist das Ergebnis:
BildHier sind einige Beobachtungen aus dem obigen Test:
- Die Ausgabe über die vorhandenen Dateien wird korrekt an die Zieldatei gesendet.
- Der Fehler (der angezeigt wird, wenn ich versuche, etwas aufzulisten, das nicht existiert) wird an den Bildschirm gesendet. Dies ist der Standardort, an den Fehler gesendet werden, es sei denn, Sie leiten sie um.
[ Laden Sie einen Bash Shell Scripting Cheat Sheet herunter. ]
Als Nächstes leite ich die Fehlerausgabe um, indem ich explizit auf Dateideskriptor 2 verweise mit ls file* non-existing-file* > my_normal_output.txt 2> my_error_output.txt:
Im obigen Beispiel:
- Die
ls
Befehl zeigt die Fehlermeldung nicht wie zuvor auf dem Bildschirm an. - Die normale Ausgabe enthält, was ich erwarte.
- Die Fehlermeldung wird an die
my_error_output.txt
gesendet Datei.
3. Senden Sie STDOUT und STDERR an dieselbe Datei
Eine andere übliche Situation besteht darin, sowohl STDOUT als auch STDERR an dasselbe zu senden Datei:
$ls file* non-existing-file* > my_consolidated_output.txt 2>&1
$ cat my_consolidated_output.txt
ls: cannot access 'non-existing-file*': No such file or directory
file1.txt
file2.txt
file3.txt
In diesem Beispiel werden alle Ausgaben (normal und Fehler) an dieselbe Datei gesendet.
Der 2>&1
Konstruktion bedeutet "sende das STDERR an die gleiche Stelle, an die du das STDOUT sendest ."
4. Ausgabe umleiten, aber Datei anhängen
In allen vorherigen Beispielen habe ich immer, wenn ich eine Ausgabe umgeleitet habe, einen einzelnen >
verwendet , was bedeutet "etwas an diese Datei senden und die Datei von Grund auf neu starten ." Als Ergebnis wird die Zieldatei, falls vorhanden, überschrieben.
Wenn ich anhängen möchte zu einer vorhandenen Datei muss ich >>
verwenden . Wenn die Datei noch nicht existiert, wird sie erstellt:
$echo "Adding stuff to the end of a file" >> my_output.txt
$cat my_output.txt
file1.txt
file2.txt
file3.txt
Adding stuff to the end of a file
5. Umleitung zu einem anderen Prozess oder ins Nirgendwo
Die obigen Beispiele behandeln die Umleitung der Ausgabe in eine Datei, aber Sie können Ausgaben auch an andere Prozesse oder dev/null
umleiten .
Das Senden von Ausgaben an andere Prozesse ist eine der leistungsstärksten Funktionen einer Shell. Verwenden Sie für diese Aufgabe den |
(Pipe)-Symbol, das die Ausgabe eines Befehls an die Eingabe des nächsten Befehls sendet:
ps -ef | grep chrome | grep -v grep | wc -l
21
Das obige Beispiel listet meine Prozesse auf und filtert alle, die die Zeichenfolge chrome enthalten , ignoriert die Zeile über mein grep
Befehl und zählt die resultierenden Zeilen. Wenn ich die Ausgabe an eine Datei senden möchte, füge ich >
hinzu und einen Dateinamen am Ende der Kette.
Schließlich ist hier ein Beispiel, in dem ich eine der Ausgaben ignorieren möchte, die STDERR:
$tar cvf my_files.tar file* more-non-existing*
file1.txt
file2.txt
file3.txt
tar: more-non-existing*: Cannot stat: No such file or directory
tar: Exiting with failure status due to previous errors
Denn der tar
Der Befehl hat keine Dateien gefunden, deren Namen mit more-non-existing
beginnen , einige Fehlermeldungen werden am Ende angezeigt.
Angenommen, ich erstelle einige Skripte und kümmere mich nicht darum, diese Fehler zu sehen oder zu erfassen (ich weiß, dass Sie im wirklichen Leben die Fehler verhindern und behandeln sollten, anstatt sie einfach zu ignorieren):
$tar cvf my_files.tar file* more-non-existing* 2> /dev/null
file1.txt
file2.txt
file3.txt
Der /dev/null
ist eine spezielle Gerätedatei, die wie ein "schwarzes Loch" ist:Was Sie dorthin senden, verschwindet einfach.
[ Laden Sie diese Anleitung zur Installation von Anwendungen unter Linux herunter. ]
6. Verwenden Sie die Weiterleitung in einem Skript
Bild
=== SUMMARY OF INVESTIGATION OF chrome ===
Date/Time of the execution: 2022-03-25 18:05:50
Number of processes found.: 5
PIDs:
1245475
1249558
1316941
1382460
1384452
Dieses sehr einfache Skript macht Folgendes:
- Zeile 3:Führt einen Befehl im Betriebssystem aus und speichert in der Variablen DATE_TIME.
- Zeile 6:Führt
ps
aus Befehl und leitet zugrep
weiter und in eine Datei.- Anstatt die Ausgabe an eine Datei zu senden, könnte ich sie an eine Variable senden (wie in Zeile 3), aber in diesem Fall möchte ich andere Aktionen mit derselben Ausgabe ausführen, also erfasse ich sie. In einer realistischeren Situation könnte es auch während der Entwicklung oder Fehlerbehebung hilfreich sein, die Datei zu haben, damit ich leichter untersuchen kann, was der Befehl generiert.
- Zeile 8:Führt zusätzliche Befehle aus, leitet die Ausgaben nach
wc
um , und weist das Ergebnis einer Variablen zu. - Zeile 9:Benutzt
awk
um nur Spalte 2 aus der Ausgabe auszuwählen und sie in absteigender Reihenfolge zu sortieren (nur um eine weitere Pipe hinzuzufügen).
Abschluss
Dies waren einige Beispiele für die Umleitung von STDOUT und STDERR. Wenn Sie all dies zusammenfassen, erkennen Sie, wie mächtig Umleitung sein kann. Indem Sie einzelne Befehle verketten, ihre Ausgabe manipulieren und das Ergebnis als Eingabe für den nächsten Befehl verwenden, können Sie Aufgaben ausführen, für die Sie andernfalls ein Skript oder Programm entwickeln müssten. Sie könnten die Technik auch in andere Skripte integrieren und alles als Bausteine verwenden.