Ukryć wynik polecenia powłoki tylko w przypadku powodzenia?

12

Ukrywanie danych wyjściowych polecenia powłoki zwykle wymaga przekierowania stderr i stdout. Czy jest jakieś wbudowane narzędzie lub polecenie, które domyślnie ukrywa dane wyjściowe, ale w przypadku błędu zrzuca całe zgromadzone dane wyjściowe? Chciałbym uruchomić to jako opakowanie zdalnych sshpoleceń. Teraz używam ich przekierowania, ale nie mam pojęcia, co spowodowało, że zawiodły, a oni są po prostu zbyt gadatliwi.

EDYCJA: W końcu stworzyłem następujący szablon na podstawie odpowiedzi @Belmin, którą trochę poprawiłem, aby zgromadzić wszystkie poprzednie polecenia ze skryptu, użyć bieżącego identyfikatora procesu, automatycznie usunąć dziennik i dodać błąd czerwony błąd wiadomość, gdy coś pójdzie nie tak. W tym szablonie początkowe silentopakowania zakończą się powodzeniem, a następnie zakończą się niepowodzeniem trzeciego polecenia, ponieważ katalog już istnieje:

#!/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
Grzegorz Adam Hankiewicz
źródło
2
Jeśli przekierujesz tylko stdout, stderr nadal będzie się pojawiał; czy to ci wystarcza, czy też chcesz zobaczyć standardowe wyjście, jeśli wystąpi błąd?
Kromey
Chcę zobaczyć oba, ale tylko wtedy, gdy coś pójdzie nie tak, inaczej nie chcę nic widzieć.
Grzegorz Adam Hankiewicz
2
To, co robię, to wypisywanie stdout i stderr na plik dziennika, aby nie zaśmiecał ekranu. Drukuję też stderr na ekranie, więc jeśli jest błąd i go widzę. Jeśli potrzebuję szczegółów, mogę sprawdzić plik dziennika, który zawiera dane wyjściowe programu i błędy programu w kontekście. W ten sposób „widzę oba, ale tylko jeśli coś pójdzie nie tak”. czy to pomaga? Zobacz stackoverflow.com/questions/2871233/…
Stefan Lasiewski
1
Czy bezpiecznie jest przekierować stderr i stdout do tego samego pliku za pomocą dwóch oddzielnych przekierowań? Pomyślałem, że zawsze powinniśmy używać 2>&1czegoś takiego:$* >>"${SILENT_LOG}" 2>&1" || report_and_exit
psmith

Odpowiedzi:

3

Ja bym tylko konfiguracja funkcja bash tak:

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; }

Następnie możesz po prostu uruchomić polecenie:

suppress foo -a bar
Belmin Fernandez
źródło
Osoba atakująca, która nie ma dostępu użytkownika root, może próbować utworzyć dowiązanie symboliczne między poleceniem rm a poleceniem, które wskazywałoby na /etc/passswdinny plik krytyczny i nadpisało zawartość.
Mitar
1
BTW, kolejność przekierowań powinna być następująca: $* > /tmp/surpress.out 2>&1To naprawdę przechwytuje stderr.
Mitar
2
$*nie jest najlepszy do obsługi dowolnych danych wejściowych. Zwłaszcza gdy zawiera spacje lub flagi. Najbardziej przenośny jest ${1+"$@"}według stackoverflow.com/questions/743454/…
balrok
Zmodyfikowany dla obu komentarzy. Dzięki --- dobra informacja.
Belmin Fernandez
7

W tym celu powinno być dość łatwo napisać skrypt.

Coś takiego jak ten całkowicie nieprzetestowany skrypt.

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

Z drugiej strony dla poleceń, które uruchamiam jako część skryptu, zwykle chcę czegoś lepszego niż po prostu wydrukować wszystkie dane wyjściowe. Często ograniczam to, co widzę, do nieznanego. Oto skrypt, który zaadaptowałem z czegoś, co przeczytałem ponad dziesięć lat temu.

#!/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 $?
Zoredache
źródło
5

Nie sądzę, że istnieje czysty sposób na zrobienie tego, jedyne, o czym mogę myśleć

  • Przechwyć wynik polecenia.
  • Sprawdź wartość zwracaną polecenia i jeśli nie powiodło się
    • wyświetlić przechwycone wyjście.

Wdrożenie tego może być ciekawym projektem, ale być może wykraczającym poza pytania i odpowiedzi.

użytkownik9517
źródło
Powinno być wykonalne za pomocą funkcji. Hm, pozwól mi spróbować.
Belmin Fernandez,
1

krótko mówiąc z czymś takim tehcommand &>/tmp/$$ || cat /tmp/$$

zależy od tego, ile użyteczności / pisania potrzebujesz / potrzebujesz. (np. użycie go jako potoku lub przekazanie polecenia argumentem)

Krótki skrypt @zoredache to po prostu proto-wrapper do tego, co dałoby większą niezawodność, obsługiłoby współbieżność itp.

rogerovo
źródło
1

Spróbuj tak:

out=`command args...` || echo $out
użytkownik2743554
źródło
2
out="$(command args...)" || echo "$out"
Napisałbym