Die Ausgabe eines Shell-Befehls nur bei Erfolg verbergen?


12

Das Ausblenden der Ausgabe eines Shell-Befehls umfasst normalerweise das Umleiten von stderr und stdout. Gibt es eine eingebaute Funktion oder einen Befehl, der die Ausgabe standardmäßig verbirgt, aber bei einem Fehler die gesamte akkumulierte Ausgabe ausgibt? Ich möchte dies als Wrapper für Remote- sshBefehle ausführen. Jetzt verwenden sie die Umleitung, aber ich bekomme keine Ahnung, warum sie fehlgeschlagen sind, und sie sind einfach zu ausführlich.

BEARBEITEN: Am Ende habe ich die folgende Vorlage basierend auf der Antwort von @Belmin erstellt, die ich ein wenig optimiert habe, um alle vorherigen Befehle aus dem Skript zu akkumulieren, die aktuelle Prozesskennung zu verwenden, das Protokoll automatisch zu entfernen und einen roten Fehler hinzuzufügen Nachricht, wenn etwas schief geht. In dieser Vorlage sind die ersten silentWrapper erfolgreich, und der dritte Befehl schlägt fehl, da das Verzeichnis bereits vorhanden ist:

#!/bin/sh

set -e

SILENT_LOG=/tmp/silent_log_$$.txt
trap "/bin/rm -f $SILENT_LOG" EXIT

function report_and_exit {
    cat "${SILENT_LOG}";
    echo "\033[91mError running command.\033[39m"
    exit 1;
}

function silent {
    $* 2>>"${SILENT_LOG}" >> "${SILENT_LOG}" || report_and_exit;
}

silent mkdir -v pepe
silent mkdir -v pepe2
silent mkdir -v pepe
silent mkdir -v pepe2

2
Wenn Sie nur stdout umleiten, wird stderr weiterhin angezeigt. reicht dir das aus, oder willst du auch stdout sehen, wenn ein fehler auftritt?
Kromey

Ich möchte beides sehen, aber nur, wenn etwas schief geht, sonst möchte ich nichts sehen.
Grzegorz Adam Hankiewicz

2
Ich drucke stdout & stderr in eine Protokolldatei, damit der Bildschirm nicht überladen wird. Ich drucke auch stderr auf den Bildschirm, wenn es also einen Fehler gibt, kann ich ihn sehen. Wenn ich Details benötige, kann ich die Logdatei überprüfen, die die Programmausgabe und die Programmfehler im Kontext enthält. Auf diese Weise kann ich "beides sehen, aber nur, wenn etwas schief geht". Hilft das? Siehe stackoverflow.com/questions/2871233/…
Stefan Lasiewski

1
Ist es sicher, stderr und stdout mit zwei separaten Weiterleitungen auf dieselbe Datei umzuleiten? Ich dachte, wir sollten immer so 2>&1etwas verwenden:$* >>"${SILENT_LOG}" 2>&1" || report_and_exit
Psmith

Antworten:


3

Ich würde eine Bash-Funktion wie folgt einrichten :

function suppress { /bin/rm --force /tmp/suppress.out 2> /dev/null; ${1+"$@"} > /tmp/suppress.out 2>&1 || cat /tmp/suppress.out; /bin/rm /tmp/suppress.out; }

Dann können Sie einfach den Befehl ausführen:

suppress foo -a bar

Ein Angreifer, der keinen Root-Zugriff auf Ihr System hat, könnte versuchen, eine Verknüpfung zwischen rm und Befehlsaufruf herzustellen, die auf eine /etc/passswdoder eine andere wichtige Datei verweist und den Inhalt überschreibt.
Mitar

1
Übrigens sollte die Reihenfolge der Weiterleitungen wie folgt lauten: $* > /tmp/surpress.out 2>&1Dies erfasst wirklich die stderr.
Mitar

2
$*ist nicht das Beste, um willkürliche Eingaben zu verarbeiten. Besonders wenn es Leerzeichen oder Flags enthält. Am portabelsten ist es ${1+"$@"}laut stackoverflow.com/questions/743454/…
balrok

Geändert nach beiden Kommentaren. Danke --- gute Infos.
Belmin Fernandez


7

Es sollte einfach genug sein, ein Skript für diesen Zweck zu schreiben.

So etwas wie dieses völlig ungetestete Skript.

OUTPUT=`tempfile`
program_we_want_to_capture &2>1 > $OUTPUT
[ $? -ne 0 ]; then
    cat $OUTPUT
    exit 1
fi
rm $OUTPUT

Andererseits möchte ich bei Befehlen, die ich als Teil eines Skripts ausführe, normalerweise etwas Besseres, als einfach die gesamte Ausgabe zu drucken. Ich beschränke das, was ich sehe, oft auf das Unbekannte. Hier ist ein Skript, das ich von etwas adaptiert habe, das ich vor über einem Jahrzehnt gelesen habe.

#!/bin/bash

the_command 2>&1 | awk '
BEGIN \
{
  # Initialize our error-detection flag.
  ErrorDetected = 0
}
# Following are regex that will simply skip all lines
# which are good and we never want to see
/ Added UserList source/ || \
/ Added User/ || \
/ init domainlist / || \
/ init iplist / || \
/ init urllist / || \
/ loading dbfile / || \
/^$/ {next} # Uninteresting message.  Skip it.

# Following are lines that we good and we always want to see
/ INFO: ready for requests / \
{
  print "  " $0 # Expected message we want to see.
  next
}

# any remaining lines are unexpected, and probably error messages.  These will be printed out and highlighted.
{
  print "->" $0 # Unexpected message.  Print it
  ErrorDetected=1
}

END \
{
  if (ErrorDetected == 1) {
    print "Unexpected messages (\"->\") detected in execution."
    exit 2
  }
}
'
exit $?

5

Ich glaube nicht, dass es dafür eine saubere Methode gibt. Ich kann mir nur Folgendes vorstellen

  • Erfassen Sie die Ausgabe des Befehls.
  • Überprüfen Sie den Rückgabewert des Befehls und ob er fehlgeschlagen ist
    • Anzeige der erfassten Ausgabe.

Die Implementierung dieses Projekts könnte zwar ein interessantes Projekt sein, geht aber möglicherweise über Fragen und Antworten hinaus.


Sollte mit einer Funktion machbar sein. Hm, lass es mich versuchen.
Belmin Fernandez

1

kurz mit so etwas wie tehcommand &>/tmp/$$ || cat /tmp/$$

hängt davon ab, wie viel Benutzerfreundlichkeit / Typisierung Sie wollen / brauchen. (zB als Pipe verwenden oder den Befehl als Argument übergeben)

@zoredache short script ist im Grunde genommen ein Proto-Wrapper dafür, der mehr Robustheit bietet, die Parallelität handhabt usw


1

Versuchen Sie es so:

out=`command args...` || echo $out

2
Ich würde es alsout="$(command args...)" || echo "$out"
Kasperd
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.