Angenommen, ich habe ein Skript wie das folgende:
nutzlos.sh
echo "This Is Error" 1>&2
echo "This Is Output"
Und ich habe noch ein Shell-Skript:
auchUseless.sh
./useless.sh | sed 's/Output/Useless/'
Ich möchte "This Is Error" oder einen anderen stderr aus nutzlos.sh in einer Variablen erfassen. Nennen wir es FEHLER.
Beachten Sie, dass ich stdout für etwas verwende. Ich möchte stdout weiterhin verwenden, daher ist es in diesem Fall nicht hilfreich, stderr in stdout umzuleiten.
Also im Grunde möchte ich tun
./useless.sh 2> $ERROR | ...
aber das funktioniert offensichtlich nicht.
Ich weiß auch, dass ich es tun könnte
./useless.sh 2> /tmp/Error
ERROR=`cat /tmp/Error`
aber das ist hässlich und unnötig.
Wenn hier keine Antworten auftauchen, muss ich das leider tun.
Ich hoffe, es gibt einen anderen Weg.
Hat jemand bessere Ideen?
ERROR=$(./useless.sh | sed 's/Output/Useless/' 2>&1 1>/dev/ttyX)
Antworten:
Es wäre ordentlicher, die Fehlerdatei folgendermaßen zu erfassen:
Die Shell erkennt dies und muss nicht '
cat
' ausführen, um die Daten abzurufen.Die größere Frage ist schwer. Ich glaube nicht, dass es einen einfachen Weg gibt, dies zu tun. Sie müssten die gesamte Pipeline in die Sub-Shell einbauen und schließlich die endgültige Standardausgabe an eine Datei senden, damit Sie die Fehler in die Standardausgabe umleiten können.
Beachten Sie, dass das Semikolon benötigt wird (in klassischen Muscheln - Bourne, Korn - sicher; wahrscheinlich auch in Bash). Das '
{}
' führt eine E / A-Umleitung über die beigefügten Befehle durch. Wie geschrieben, würde es auch Fehler von erfassensed
.quelle
/dev/null
stattdessen umleiten zuoutfile
(Wenn Sie wie ich sind, haben Sie diese Frage über Google gefunden und haben nicht die gleichen Anforderungen wie das OP)stdout
undstderr
hin und her. Aber Vorsicht , wie hier gesagt wird: In Bash ist es besser, nicht anzunehmen, dass Dateideskriptor 3 nicht verwendet wird " .auchUseless.sh
Auf diese Weise können Sie die Ausgabe Ihres
useless.sh
Skripts über einen Befehl wie beispielsweisesed
leiten undstderr
in einer Variablen mit dem Namen speichernerror
. Das Ergebnis der Pipe wirdstdout
zur Anzeige gesendet oder an einen anderen Befehl weitergeleitet.Es werden einige zusätzliche Dateideskriptoren eingerichtet, um die dafür erforderlichen Umleitungen zu verwalten.
quelle
stderr
undstdout
in Variablen erfassen ?dry_run
Funktion zu implementieren , die zuverlässig zwischen dem Echo ihrer Argumente und deren Ausführung wählen kann, unabhängig davon, ob der Befehl, der trocken ausgeführt wird, an eine andere Datei weitergeleitet wird.read
keine Eingaben von einer Pipe. Sie können andere Techniken verwenden, um das zu erreichen, was Sie demonstrieren möchten.Umgeleitetes stderr nach stdout, stdout nach / dev / null und dann die Backticks verwenden oder
$()
das umgeleitete stderr erfassen:quelle
PY_VERSION="$(python --version 2>&1)"
Es gibt viele Duplikate für diese Frage, von denen viele ein etwas einfacheres Verwendungsszenario haben, in dem Sie nicht gleichzeitig stderr und stdout und den Exit-Code erfassen möchten .
funktioniert für das allgemeine Szenario, in dem Sie im Erfolgsfall entweder eine ordnungsgemäße Ausgabe oder im Fehlerfall eine Diagnosemeldung auf stderr erwarten.
Beachten Sie, dass die Steueranweisungen der Shell bereits
$?
unter der Haube geprüft werden . also alles was aussiehtist nur eine ungeschickte, unidiomatische Art zu sagen
quelle
quelle
command
ist hier eine schlechte Wahl, da es tatsächlich einen eingebauten Namen gibt. Könnte esyourCommand
oder so machen, um expliziter zu sein.Zum Nutzen des Lesers dieses Rezept hier
Wenn Sie fangen wollen
stderr
einigecommand
invar
können Sie tunDanach haben Sie alles:
Wenn
command
es einfach ist (nicht so ähnlicha | b
), können Sie das Innere{}
weglassen:Eingewickelt in eine einfach wiederverwendbare Funktion
bash
(benötigt wahrscheinlich Version 3 und höher fürlocal -n
):Erklärt:
local -n
Aliase "$ 1" (das ist die Variable fürcatch-stderr
)3>&1
verwendet den Dateideskriptor 3, um dort Standardpunkte zu speichern{ command; }
(oder "$ @") führt dann den Befehl innerhalb der Ausgabeerfassung aus$(..)
2>&1
leitetstderr
zur Ausgabeerfassung weiter$(..)
1>&3
leitetstdout
von der Ausgabeerfassung$(..)
zurück zu der "äußeren",stdout
die in Dateideskriptor 3 gespeichert wurde. Beachten Sie, dass sichstderr
immer noch darauf bezieht, wo FD 1 zuvor gezeigt hat: Zur Ausgabeerfassung$(..)
3>&-
schließt dann den Dateideskriptor 3, da er nicht mehr benötigt wird, so dasscommand
nicht plötzlich ein unbekannter offener Dateideskriptor angezeigt wird. Beachten Sie, dass in der Außenhülle FD 3 noch geöffnet ist, es jedochcommand
nicht angezeigt wird.lvm
über unerwartete Dateideskriptoren beschweren. Undlvm
beschwert sich beistderr
- genau das, was wir einfangen werden!Sie können mit diesem Rezept jeden anderen Dateideskriptor abfangen, wenn Sie ihn entsprechend anpassen. Außer Dateideskriptor 1 natürlich (hier wäre die Umleitungslogik falsch, aber für Dateideskriptor 1 können Sie einfach
var=$(command)
wie gewohnt verwenden).Beachten Sie, dass dies den Dateideskriptor 3 opfert. Wenn Sie diesen Dateideskriptor benötigen, können Sie die Nummer jederzeit ändern. Beachten Sie jedoch, dass einige Muscheln (aus den 1980er Jahren) möglicherweise
99>&1
als Argument verstanden werden,9
gefolgt von9>&1
(dies ist kein Problem fürbash
).Beachten Sie auch, dass es nicht besonders einfach ist, diesen FD 3 über eine Variable konfigurierbar zu machen. Das macht die Dinge sehr unlesbar:
Anmerkungen:
catch-var-from-fd-by-fd var 2 3 cmd..
ist das gleiche wiecatch-stderr var cmd..
shift || return
Dies ist nur eine Möglichkeit, hässliche Fehler zu vermeiden, falls Sie vergessen, die richtige Anzahl von Argumenten anzugeben. Vielleicht wäre das Beenden der Shell eine andere Möglichkeit (dies erschwert jedoch das Testen über die Befehlszeile).exec
, aber dann wird sie wirklich hässlich.bash
sodass keine Notwendigkeit bestehtlocal -n
. Dann können Sie jedoch keine lokalen Variablen verwenden und es wird extrem hässlich!eval
s auf sichere Weise verwendet werden. Wird normalerweiseeval
als gefährlich angesehen. In diesem Fall ist es jedoch nicht böser als die Verwendung"$@"
(zum Ausführen beliebiger Befehle). Bitte achten Sie jedoch darauf, das genaue und korrekte Zitat wie hier gezeigt zu verwenden (sonst wird es sehr, sehr gefährlich ).quelle
So habe ich es gemacht:
Anwendungsbeispiel:
Es wird eine temporäre Datei verwendet. Aber zumindest das hässliche Zeug ist in eine Funktion eingewickelt.
quelle
eval
. Es besteht beispielsweiseprintf -v "$1" '%s' "$(<tmpFile)"
kein Risiko, beliebigen Code auszuführen, wenn IhreTMPDIR
Variable auf einen schädlichen Wert festgelegt wurde (oder der Name Ihrer Zielvariablen einen solchen Wert enthält).rm -- "$tmpFile"
ist robuster alsrm $tmpFile
.Dies ist ein interessantes Problem, für das ich gehofft habe, dass es eine elegante Lösung gibt. Leider habe ich eine ähnliche Lösung wie Herr Leffler, aber ich möchte hinzufügen, dass Sie innerhalb einer Bash-Funktion für eine bessere Lesbarkeit nutzlos aufrufen können:
Alle anderen Arten der Ausgabeumleitung müssen durch eine temporäre Datei gesichert werden.
quelle
POSIX
STDERR kann mit etwas Umleitungsmagie erfasst werden:
Beachten Sie, dass die Weiterleitung von STDOUT des Befehls (hier
ls
) innerhalb des Innersten erfolgt{
}
. Wenn Sie einen einfachen Befehl ausführen (z. B. keine Pipe), können Sie diese inneren Klammern entfernen.Sie können nicht außerhalb des Befehls weiterleiten , da durch Piping eine Unterschale in
bash
und erstelltzsh
wird und die Zuweisung zur Variablen in der Unterschale für die aktuelle Shell nicht verfügbar ist.Bash
In
bash
ist es besser, nicht anzunehmen, dass der Dateideskriptor 3 nicht verwendet wird:Beachten Sie, dass dies nicht funktioniert
zsh
.Vielen Dank an diese Antwort für die allgemeine Idee.
quelle
Dieser Beitrag hat mir geholfen, eine ähnliche Lösung für meine eigenen Zwecke zu finden:
Solange unsere NACHRICHT keine leere Zeichenfolge ist, geben wir sie an andere Personen weiter. Dadurch erfahren wir, ob unsere format_logs.py mit einer Python-Ausnahme fehlgeschlagen ist.
quelle
Erfassen UND Drucken stderr
Nervenzusammenbruch
Sie können
$()
stdout erfassen, möchten aber stattdessen stderr erfassen. Sie tauschen also stdout und stderr aus. Verwendung von fd 3 als temporärer Speicher im Standard-Swap-Algorithmus.Wenn Sie erfassen UND drucken möchten, verwenden Sie
tee
, um ein Duplikat zu erstellen. In diesem Fall wird die Ausgabe vontee
erfasst$()
und nicht an die Konsole gesendet, aber stderr (oftee
) wird weiterhin an die Konsole gesendet, sodass wir diese als zweite Ausgabetee
über die spezielle Datei verwenden,/dev/fd/2
datee
ein Dateipfad anstelle eines fd erwartet wird Nummer.HINWEIS: Das sind sehr viele Weiterleitungen in einer einzelnen Zeile, und die Reihenfolge ist wichtig.
$()
greift nach dem Standard vontee
am Ende der Pipeline und die Pipeline selbst leitet den Standard von./useless.sh
zum Standard vontee
AFTER, nachdem wir Standard und Standard ausgetauscht haben./useless.sh
.Verwenden von stdout von ./useless.sh
Das OP sagte, er wolle immer noch stdout verwenden (nicht nur drucken), wie
./useless.sh | sed 's/Output/Useless/'
.Kein Problem, tun Sie es einfach, bevor Sie stdout und stderr tauschen. Ich empfehle, es in eine Funktion oder Datei (also-useless.sh) zu verschieben und dies anstelle von ./useless.sh in der obigen Zeile aufzurufen.
Wenn Sie jedoch stdout UND stderr erfassen möchten, müssen Sie meiner Meinung nach auf temporäre Dateien zurückgreifen, da jeweils
$()
nur eine ausgeführt wird und eine Unterschale erstellt wird, von der Sie keine Variablen zurückgeben können.quelle
Ich habe ein wenig über Tom Hales Antwort nachgedacht und festgestellt, dass es möglich ist, das Umleitungs-Yoga in eine Funktion zu verpacken, um die Wiederverwendung zu vereinfachen. Beispielsweise:
Es ist mit ziemlicher Sicherheit möglich, dies weiter zu vereinfachen. Ich habe nicht besonders gründlich getestet, aber es scheint sowohl mit Bash als auch mit Ksh zu funktionieren.
quelle
Wenn Sie die Verwendung einer temporären Datei umgehen möchten, können Sie möglicherweise die Prozessersetzung verwenden. Ich habe es noch nicht ganz zum Laufen gebracht. Dies war mein erster Versuch:
Dann habe ich es versucht
jedoch
Die Prozessersetzung macht also im Allgemeinen das Richtige ... Leider verliere ich den Inhalt von , wenn ich STDIN
>( )
mit etwas einpacke$()
, um dies in einer Variablen zu erfassen$()
. Ich denke, das liegt daran, dass$()
ein Unterprozess gestartet wird, der keinen Zugriff mehr auf den Dateideskriptor in / dev / fd hat, der dem übergeordneten Prozess gehört.Durch die Prozessersetzung habe ich die Möglichkeit erhalten, mit einem Datenstrom zu arbeiten, der nicht mehr in STDERR enthalten ist. Leider kann ich ihn nicht so bearbeiten, wie ich es möchte.
quelle
./useless.sh 2> >( ERROR=$( cat <() ); echo "$ERROR" )
würden, würden Sie die Ausgabe von sehenERROR
. Das Problem besteht darin, dass die Prozessersetzung in einer Unter-Shell ausgeführt wird, sodass der in der Unter-Shell festgelegte Wert die übergeordnete Shell nicht beeinflusst.quelle
a=> b=>stderr
a
es in einer Sub-Shell ausgewertet und zugewiesen wird und die Zuweisung in der Sub-Shell die übergeordnete Shell nicht beeinflusst. (Getestet unter Ubuntu 14.04 LTS sowie Mac OS X 10.10.1.)GNU bash, version 4.4.12(1)-release (x86_64-pc-msys)
)SLE 11.4
und erzeugt den von @JonathanLefflerIn zsh:
quelle
Zum Fehlerprüfen Ihrer Befehle:
Inspiriert von Lean Manufacturing:
quelle
if
. Lassen Sie mich eine separate Lösung veröffentlichen.Eine einfache Lösung
Wird herstellen:
quelle
Verbesserung der Antwort von YellowApple :
Dies ist eine Bash-Funktion zum Erfassen von stderr in einer beliebigen Variablen
stderr_capture_example.sh
::Testen:
Ausgabe:
Diese Funktion kann verwendet werden, um die zurückgegebene Auswahl eines
dialog
Befehls zu erfassen .quelle