Jak mogę wyłączyć dane wyjściowe tylko wtedy, gdy polecenie się powiedzie?

22

Chciałbym uprościć dane wyjściowe skryptu, tłumiąc wyniki poleceń pomocniczych, które zwykle są skuteczne.

Jednak użycie -qna nich ukrywa dane wyjściowe, gdy czasami zawodzą, więc nie mam możliwości zrozumienia błędu. Ponadto te polecenia rejestrują swoje dane wyjściowe stderr.

Czy istnieje sposób na wyłączenie wyniku polecenia tylko, jeśli się powiedzie ?

Na przykład (ale nie tylko) coś takiego:

mycommand | fingerscrossed

Jeśli wszystko pójdzie dobrze, fingerscrossedłapie wyjście i odrzuca je. W przeciwnym razie powtarza to na wyjściu standardowym lub wyjściowym błędu (cokolwiek).

Matthieu Napoli
źródło

Odpowiedzi:

36

moreutilschronicpolecenie robi tylko to:

chronic mycommand

połknie mycommandwyjście, chyba że zawiedzie, w którym to przypadku wyjście zostanie wyświetlone.

Stephen Kitt
źródło
1
Dziękuję Ci. Rozumiem, że nie jest domyślnie instalowany w większości systemów operacyjnych Unix?
Matthieu Napoli
1
Prawdopodobnie nie, chociaż jest szeroko zapakowany, więc powinien być łatwy w instalacji.
Stephen Kitt
1
Debian ma to w pakiecie moreutils. W każdym razie fajnie dla mnie :)
Tom Zych,
6
Zauważ, że przechowuje całe wyjście w pamięci.
Stéphane Chazelas,
1
@ StéphaneChazelas, który jest prawdopodobnie jedynym sposobem na zaimplementowanie czegoś takiego, dane wyjściowe muszą być przechowywane, gdy polecenie jest uruchomione na wypadek, gdyby było potrzebne.
Centimane
11
### do this bit once at the top of your script
divert=
exec 3<>"${divert:=$(mktmp)}" 4<>/dev/null
rm -- "$divert"; unset divert
### then do this bit as often as needed
command >&3 2>&3
cat <&3 >&"$(((RTN=$?)?2:4))"

To prawdopodobnie powinno załatwić sprawę. Buforuje dane wyjściowe każdego z nich commanddo usuniętego pliku tymczasowego, a następnie wysysa dane wyjściowe do jednego /dev/nulllub do stderr, w zależności od tego, czy jego status powrotu nie był równy zero. Ponieważ plik tymczasowy jest usuwany przed czasem, nie może zostać odczytany przez żaden proces, ale bieżąca powłoka i jej elementy potomne na swoim deskryptorze pliku (z wyjątkiem podstępnych /proc/$pid/fdszpiegów z odpowiednimi uprawnieniami) , i nie wymaga czyszczenia po zakończeniu .

Być może wygodniejsze rozwiązanie w systemach Linux:

divert(){
    "$@" >&3 2>&3 ||
    eval "cat <&3
          return $?"
}   3<<"" 3<>/dev/fd/3

..., które w większości powłok, działa podobnie jak drugi, poza tym, że można nazwać to tak: divert some simple-command with args. Strzeż wysokich poleceń wyjściowych w "$@", choć dla dash, yashlub jakieś inne pociski, które zrobienia-dokumentów z rurami - Myślę, że może to być możliwe w tych muszli wypełnić bufor rury (przy domyślnie około 128kb na dystrybucji Linksa) i tak impasu . To nie powinno być zmartwieniem ksh, mksh, bash, zsh, lub powłoka Bourne'a, choć - wszyscy ci zrobić w zasadzie to samo co ja wyraźnie powyżej exec.

mikeserv
źródło
9

Zwykle w przypadku błędu polecenie wyświetla komunikaty, stderrwięc dla zadania, które możesz po prostu pominąćstdout

mycommand > /dev/null
Costas
źródło
9
Bądź ostrożny
PyRulez,
Dziękuję, ale jak powiedziałem w pytaniu, moje polecenia rejestrują wszystkie dane wyjściowe stderr(więc nie ma to wpływu).
Matthieu Napoli
4

Aby stworzyć własną chroniczną

my_chronic() {
  tmp=$(mktemp) || return # this will be the temp file w/ the output
  "$@"  > "$tmp" 2>&1 # this should run the command, respecting all arguments
  ret=$?
  [ "$ret" -eq 0 ] || cat "$tmp"  # if $? (the return of the last run command) is not zero, cat the temp file
  rm -f "$tmp"
  return "$ret" # return the exit status of the command
}
Jacob Minshall
źródło
3

Robię coś takiego w moich plikach makefile:

if (mycommand) &> mycommand.log; then 
  echo success 
else 
  c=$?; 
  echo;echo -e "Bad result from previous command, see mycommand.log for more details";echo;
  command_to_run_on_fail
  (exit $c)
fi

Dostosowując to do swojej sytuacji, możesz zrobić coś takiego:

if ! (mycommand) &> mycommand.log; then 
  c=$?; 
  cat mycommand.log
  rm mycommand.log
  (exit $c)
fi

Zatem „if” uruchamia polecenie i przesyła dane wyjściowe do mycommand.log. Jeśli chcesz złapać stdout vs stdout vs cokolwiek, może być konieczna zmiana polecenia potoku „&>” na „>”. Jeśli polecenie nie powiedzie się, przechwyć kod błędu, wydrukuj zawartość pliku mycommand.log, usuń plik mycommand.log, a na koniec powróć z oryginalnym kodem błędu.

Bez (exit $ c) powróciłbyś z kodem wyjścia, który pasuje do tego, co zwróciło polecenie rm.

Wreszcie, jeśli chcesz mieć jedną wkładkę, coś takiego działałoby.

mycommand &> mycommand.log || cat mycommand.log; rm mycommand.log
Jordania
źródło
2
Czy naprawdę pakujesz swoje polecenia / etc. w (...)taki sposób? Ponieważ nie robi nic przydatnego, ale spawnuje dodatkowe podpowłoki.
Etan Reisner,
@EtanReisner (exit $c)ustawia $?, czego nie można zrobić inaczej. if ! (mycommand) &>xma znaczenie dla przekierowania, jeśli polecenie używa np. timelub dałoby błąd powłoki.
Michael Homer,
@MichaelHomer - dla tych rzeczy są { ; }curlies ... choć exitjest to trochę trudne, co prawda.
mikeserv
We fragmencie makefile, jeśli próbujesz wyjść z poprzednio zapisanym $?, możesz po prostu użyć, exit $cale tak, w innych przypadkach (exit $?)ma wartość (choć rret() { return $1; }generalnie funkcja powłoki byłaby lepsza, argumentowałbym). Jednak podpowłoka dla poleceń wciąż nie jest tak jak wskazywał mikeserv.
Etan Reisner,
3

Właśnie znalazłem tę o wiele prostszą odpowiedź na inne pytanie :

output=`mycommand 2>&1` || echo $output

Działa jak marzenie!

Matthieu Napoli
źródło
Uwaga: w moim przypadku było to znacznie prostsze rozwiązanie (unikaj instalowania dodatkowych rzeczy na wszystkich serwerach CI), więc postanowiłem oznaczyć ten jako zaakceptowany. YMMV.
Matthieu Napoli
Zauważ, że jeśli użyjesz set -o xtrace w skrypcie powłoki, wówczas wszystkie dane wyjściowe będą tam ponownie w ramach rejestrowania szczegółów danych wyjściowych przypisania = ... :-). W takim przypadku prawdopodobnie lepiej jest używać przewlekłego.
Jan-Philip Gehrcke,