Come posso eliminare l'output solo se il comando ha esito positivo?


22

Vorrei semplificare l'output di uno script sopprimendo l'output di comandi secondari che di solito hanno successo.

Tuttavia, l'utilizzo -qsu di essi nasconde l'output quando occasionalmente falliscono, quindi non ho modo di capire l'errore. Inoltre, questi comandi accedono al loro output stderr.

C'è un modo per sopprimere l'output di un comando solo se ha esito positivo ?

Ad esempio (ma non limitato a) qualcosa del genere:

mycommand | fingerscrossed

Se tutto va bene, fingerscrossedcattura l'output e lo scarta. Altrimenti, fa eco all'output standard o di errore (qualunque cosa).

Risposte:


36

moreutils' chroniccomando fa proprio questo:

chronic mycommand

inghiottirà mycommandl'output, a meno che non fallisca, nel qual caso viene visualizzato l'output.


1
Grazie. Presumo che non sia installato di default sulla maggior parte dei sistemi operativi Unix?
Matthieu Napoli,

1
Probabilmente no, anche se è ampiamente confezionato, quindi dovrebbe essere facile da installare.
Stephen Kitt,

1
Debian ce l'ha nel pacchetto moreutils. Bello per me, comunque :)
Tom Zych,

6
Si noti che memorizza l'intero output in memoria.
Stéphane Chazelas,

1
@ StéphaneChazelas è probabilmente l'unico modo per implementare qualcosa del genere, l'output deve essere memorizzato mentre il comando è in esecuzione nel caso sia necessario.
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))"

Questo dovrebbe probabilmente fare il trucco. Buffererà l'output di ciascuno commandin un file temporaneo cancellato, e successivamente ne sottrarrà l'output in uno /dev/nullo in uno stderr a seconda che il suo stato di ritorno sia o meno zero. Poiché il file temporaneo viene cancellato in anticipo, non può essere letto da nessun processo tranne la shell corrente e i relativi figli sul suo descrittore di file (salvo snoop /proc/$pid/fdsubdoli con autorizzazioni appropriate) e non richiede ripulitura quando hai finito.

Forse una soluzione più conveniente su sistemi linux:

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

... che, nella maggior parte dei gusci, funziona come l'altro, se non che si può chiamare come: divert some simple-command with args. Attenzione ai comandi ad alto output in "$@", sebbene per dash, yasho ad alcune altre shell che fanno qui-documenti con pipe - penso che in quelle shell sia possibile riempire il buffer delle pipe (con un valore predefinito di circa 128kb su Linux) e quindi deadlock . Che non dovrebbe essere una preoccupazione per ksh, mksh, bash, zsh, o la shell Bourne, anche se - tutti coloro che fanno fondamentalmente la stessa cosa come ho fatto in modo esplicito di cui sopra con exec.


9

In genere, in caso di errore, il comando invia i messaggi in stderrmodo che per il tuo compito puoi semplicemente sopprimerlostdout

mycommand > /dev/null

9
Fai

Grazie, ma come ho detto nella domanda i miei comandi accedono tutto l'output stderr(quindi non ha alcun effetto).
Matthieu Napoli,

4

Per rendere il tuo cronico

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
}

3

Faccio qualcosa del genere nei miei 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

Adattandolo alla tua situazione, potresti fare qualcosa del genere:

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

Quindi, "if" esegue il comando e indirizza l'output a mycommand.log. Se devi intercettare stdout vs stdout o qualsiasi altra cosa, potresti dover cambiare il comando pipe '&>' in '>'. Se il comando non riesce, acquisire il codice di errore, stampare il contenuto di mycommand.log, rimuovere mycommand.log e infine tornare con il codice di errore originale.

Senza (exit $ c) torneresti con il codice di uscita che corrisponde a quello che ha restituito il comando 'rm'.

Infine, se si desidera una fodera, qualcosa del genere funzionerebbe.

mycommand &> mycommand.log || cat mycommand.log; rm mycommand.log

2
Avvolgi davvero i tuoi comandi / ecc. in (...)quel modo? Perché non fa nulla di utile per te ma genera sub-shell extra.
Etan Reisner, il

@EtanReisner (exit $c)sta impostando $?, il che è qualcosa che non potresti fare diversamente. if ! (mycommand) &>xè significativo con il reindirizzamento se il comando usa eg timeo darebbe un errore di shell.
Michael Homer,

@MichaelHomer - per quelle cose ci sono { ; }i ricci ... anche se exitè un po 'complicato lì, è vero .
Mikeserv,

In uno snippet di makefile se stai cercando di uscire con il salvataggio precedentemente $?, puoi semplicemente usare exit $cma sì, in altri casi (exit $?)ha valore (anche se una funzione di shell rret() { return $1; }sarebbe meglio in generale, direi). La subshell per i comandi non funziona ancora, come indicato da Mikeserv.
Etan Reisner,

3

Ho appena trovato questa risposta molto più semplice su quest'altra domanda :

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

Funziona come un fascino!


Nota: per il mio caso d'uso questa era una soluzione molto più semplice (evita di installare roba extra su tutti i server CI), quindi ho scelto di contrassegnarla come accettata. YMMV.
Matthieu Napoli,

Nota che se usi set -o xtrace nello script della tua shell, tutto l'output sarà di nuovo lì come parte della registrazione dei dettagli dell'output dell'assegnazione = ... :-). In quel caso, probabilmente è meglio usare il cronico.
Jan-Philip Gehrcke,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.