Wie werden Ergebnisse von zwei verschiedenen Befehlen zu einem einzigen Befehl zusammengefasst und weitergeleitet?

35

Ich möchte die Ausgabe von zwei verschiedenen Befehlen zusammenführen und zu einem einzigen Befehl weiterleiten.

Ein dummes Beispiel:

Befehle, mit denen ich die Ausgabe zusammenführen möchte:

cat wordlist.txt
ls ~/folder/*

in:

wc -l

In diesem Beispiel möchte ich wc -l8 zurückgeben , wenn wordlist.txt 5 Zeilen und 3 Dateien enthält .

$cat wordlist.txt *[magical union thing]* ls ~/folder/* | wc -l
8

Wie mache ich das?

David Oneill
quelle

Antworten:

56

Ihre magische Vereinigungssache ist ein Semikolon ... und geschweifte Klammern:

    { cat wordlist.txt ; ls ~/folder/* ; } | wc -l

Die geschweiften Klammern gruppieren nur die Befehle, sodass sich das Pipe-Zeichen |auf die kombinierte Ausgabe auswirkt.

Sie können auch Klammern ()um eine Befehlsgruppe verwenden, um die Befehle in einer Subshell auszuführen. Dies hat eine subtile Reihe von Unterschieden mit geschweiften Klammern, z. B. probieren Sie Folgendes aus:

    cd $HOME/Desktop ; (cd $HOME ; pwd) ; pwd
    cd $HOME/Desktop ; { cd $HOME ; pwd ; } ; pwd

Sie werden feststellen, dass alle Umgebungsvariablen, einschließlich des aktuellen Arbeitsverzeichnisses, nach dem Verlassen der Gruppe in Klammern zurückgesetzt werden, jedoch nicht nach dem Verlassen der Gruppe in geschweiften Klammern.

Was das Semikolon betrifft, enthalten Alternativen die Zeichen &&und ||, die den zweiten Befehl nur dann bedingt ausführen, wenn der erste erfolgreich ist oder wenn dies nicht der Fall ist, z

    cd $HOME/project && make
    ls $HOME/project || echo "Directory not found."
pablomme
quelle
1
Das geht doch! Hilf mir zu lernen - was genau machen die geschweiften Klammern hier? Welche anderen magischen Kräfte haben sie?
David Oneill
@DavidOneill { list; }ist ein zusammengesetzter Befehl . From bash(1): list wird einfach in der aktuellen Shell-Umgebung ausgeführt. Liste muss mit einem Zeilenumbruch oder einem Semikolon abgeschlossen werden. [..] Der Rückgabestatus ist der Exit-Status der Liste.
Lekensteyn
Ich habe ein bisschen mehr Infos zur Antwort hinzugefügt. Die endgültige Anleitung zum Erstellen von Shell-Skripten mit bash ist die bash-Manpage. man bashGeben Sie sie über die Befehlszeile ein und durchsuchen Sie sie.
Pablomme
Sollte die ; in diesen Befehlen nicht && sein, falls die Datei wordlist.txt nicht existiert?
Rinzwind
Wenn wordlist.txtkein Fehler von vorhanden ist, catwird dieser bei Standardfehlern, jedoch nicht bei der Standardausgabe angezeigt, sodass wc -lkeine Zeilen von diesem Fehler gezählt werden . Gleiches gilt für ~/foldernicht vorhandene. Man könnte 2> /dev/nullzwischen jedem dieser Befehle ein Semikolon einfügen, um Rauschen bei Standardfehlern zu vermeiden, aber abgesehen davon, dass sie hässlich sind, sind Fehlermeldungen harmlos, um Zeilen zu zählen.
Pablomme
8

Da wcein Dateipfad als Eingabe akzeptiert wird, können Sie auch die Prozessersetzung verwenden:

wc -l <(cat wordlist.txt; ls ~/folder/*)

Dies entspricht in etwa:

echo wordlist.txt > temp
ls ~/folder/* >> temp
wc -l temp

Beachten Sie, dass ls ~/folder/*auch der Inhalt von Unterverzeichnissen zurückgegeben wird, falls vorhanden (aufgrund der Glob-Erweiterung). Wenn Sie nur den Inhalt auflisten möchten ~/folder, verwenden Sie einfach ls ~/folder.

Lekensteyn
quelle
Das wc -lwar nur ein erfundenes Beispiel, ich füge es tatsächlich in etwas Komplizierteres ein, das diese Option nicht hat.
David Oneill
2
@DavidOneill Nun, wie catein Datei-Argument akzeptiert, können Sie cat <(cat wordlist.txt; ls wordlist.txt) | wc -lund sogar verwenden cat wordlist.txt <(ls wordlist.txt) | wc -l. Das ist natürlich sehr hässlich, zeigt aber die unendlichen Möglichkeiten mit Kommandozeilen-Tools.
Lekensteyn
1
@DavidOneill Richtig, ich habe es jetzt korrigiert, danke.
Lekensteyn
1
Du willst ls ~/folder/also das wenn~/folder ein symbolischer Link lsden Inhalt seines Ziels anstelle des Links selbst auflistet. Übrigens sollten alle lsBefehle befolgt werden, -1damit eine einzelne Datei pro Zeile gedruckt wird und wc -leine gültige Methode zum Zählen von Dateien ist.
Pablomme
@pablomme In Bezug auf das Symlink-Ding sind Sie der Meinung, aber dies -1impliziert, dass die Ausgabe kein Terminal, sondern eine Pipe ist.
Lekensteyn,
2

Ich stellte mir die gleiche Frage und schrieb schließlich ein kurzes Drehbuch.

magicalUnionThing(Ich nenne es append):

#!/bin/sh
cat /dev/stdin
$*

Mach das Skript ausführbar

chmod +x ./magicalUnionThing

Jetzt machst du

cat wordlist.txt |./magicalUnionThing ls ~/folder/* | wc -l

Was es macht:

  • Standardeingabe an Standardausgabe senden
  • Argument ausführen. $*Gibt alle Argumente als String zurück. Die Ausgabe dieses Befehls erfolgt standardmäßig in der Skript-Standardausgabe.

Die Standardausgabe von magicalUnionThing ist also die Standardausgabe + Standardausgabe des Befehls, der als Argument übergeben wird.

Es gibt natürlich einfachere Wege, als nach anderen Antworten.
Vielleicht kann diese Alternative in einigen Fällen nützlich sein.

Rolf
quelle