Comment puis-je supprimer la sortie uniquement si la commande réussit?

22

Je voudrais simplifier la sortie d'un script en supprimant la sortie des commandes secondaires qui réussissent généralement.

Cependant, leur utilisation -qmasque la sortie lorsqu'ils échouent parfois, donc je n'ai aucun moyen de comprendre l'erreur. En outre, ces commandes enregistrent leur sortie stderr.

Existe-t-il un moyen de supprimer la sortie d'une commande uniquement si elle réussit ?

Par exemple (mais sans s'y limiter) quelque chose comme ceci:

mycommand | fingerscrossed

Si tout se passe bien, fingerscrossedcapture la sortie et la rejette. Sinon, il se répercute sur la sortie standard ou d'erreur (peu importe).

Matthieu Napoli
la source

Réponses:

36

moreutils' chroniccommande fait exactement cela:

chronic mycommand

va avaler mycommandla sortie, sauf si elle échoue, auquel cas la sortie est affichée.

Stephen Kitt
la source
1
Merci. Je suppose qu'il n'est pas installé par défaut sur la plupart des systèmes d'exploitation Unix?
Matthieu Napoli
1
Probablement pas, bien qu'il soit largement conditionné, il devrait donc être facile à installer.
Stephen Kitt
1
Debian l'a dans le paquet moreutils. Bien pour moi, quand même :)
Tom Zych
6
Notez qu'il stocke la sortie entière en mémoire.
Stéphane Chazelas
1
@ StéphaneChazelas qui est probablement le seul moyen d'implémenter quelque chose comme ça, la sortie doit être stockée pendant l'exécution de la commande au cas où elle serait nécessaire.
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))"

Cela devrait probablement faire l'affaire. Il tamponnera la sortie de chacun commanddans un fichier temporaire supprimé et siphonnera ensuite sa sortie dans l'un /dev/nullou l' autre ou dans stderr selon que son état de retour n'est pas nul. Étant donné que le fichier temporaire est supprimé à l'avance, il ne peut être lu par aucun processus, à l' exception/proc/$pid/fd du shell actuel et de ses enfants sur son descripteur de fichier (sauf les snoopy snoops avec les autorisations appropriées) , et il ne nécessite pas de nettoyage lorsque vous avez terminé .

Peut-être une solution plus pratique sur les systèmes Linux:

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

... qui, dans la plupart des coquilles, fonctionne comme l'autre, sauf que vous pouvez l' appeler comme: divert some simple-command with args. Méfiez-vous des commandes à haut rendement dans "$@", bien que pour dash, yashou d'autres shells qui font ici des documents avec des tuyaux - je pense qu'il peut être possible dans ces shells de remplir le tampon de pipe (à une valeur par défaut d'environ 128 Ko sur Linux) et ainsi de bloquer . Cela ne devrait pas être un souci pour ksh, mksh, bash, zshou le shell Bourne, bien que - tous ceux qui font essentiellement la même chose que je l' ai fait explicitement ci - dessus exec.

mikeserv
la source
9

Habituellement, en cas d'erreur, la commande génère des messages, stderrdonc pour votre tâche, vous pouvez simplement supprimerstdout

mycommand > /dev/null
Costas
la source
9
Soyez prudent
PyRulez
Merci, mais comme je l'ai dit dans la question, mes commandes enregistrent toutes les sorties stderr(donc cela n'a aucun effet).
Matthieu Napoli
4

Pour faire votre propre chronique

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
la source
3

Je fais quelque chose comme ça dans mes makefiles:

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

En adaptant cela à votre situation, vous pourriez faire quelque chose comme ceci:

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

Ainsi, "if" exécute la commande et redirige la sortie vers mycommand.log. Si vous avez besoin d'attraper stdout vs stdout vs que ce soit, vous devrez peut-être changer la commande de canal '&>' en '>'. Si la commande échoue, capturez le code d'erreur, imprimez le contenu de mycommand.log, supprimez mycommand.log et revenez enfin avec le code d'erreur d'origine.

Sans (exit $ c), vous retourneriez avec le code de sortie qui correspond à ce que la commande 'rm' a renvoyé.

Enfin, si vous voulez une doublure, quelque chose comme ça fonctionnerait.

mycommand &> mycommand.log || cat mycommand.log; rm mycommand.log
Jordan
la source
2
Enveloppez-vous vraiment vos commandes / etc. dans (...)comme ça? Parce qu'il ne fait rien d'utile pour vous mais fait apparaître des sous-coquilles supplémentaires.
Etan Reisner du
@EtanReisner (exit $c)est en train de se mettre en place $?, ce que vous ne pouvez pas faire autrement. if ! (mycommand) &>xest significatif avec la redirection si la commande utilise par exemple timeou donnerait une erreur de shell.
Michael Homer
@MichaelHomer - pour ces choses, il y a { ; }les curlies ... mais exitc'est un peu délicat, certes.
mikeserv
Dans un extrait de makefile, si vous essayez de quitter avec le fichier précédemment enregistré, $?vous pouvez simplement l'utiliser, exit $cmais oui, dans d'autres cas, il (exit $?)a de la valeur (même si une fonction shell rret() { return $1; }serait mieux en général, je dirais). Le sous-shell pour les commandes ne fonctionne toujours pas vraiment comme indiqué par mikeserv.
Etan Reisner
3

Je viens de trouver cette réponse beaucoup plus simple sur cette autre question :

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

Fonctionne comme un charme!

Matthieu Napoli
la source
Remarque: pour mon cas d'utilisation, c'était une solution beaucoup plus simple (évitez d'installer des éléments supplémentaires sur tous les serveurs CI), j'ai donc choisi de marquer celui-ci comme accepté. YMMV.
Matthieu Napoli
Notez que si vous utilisez set -o xtrace dans votre script shell, toutes les sorties seront à nouveau présentes dans le cadre de la journalisation des détails de la sortie d'affectation = ... :-). Dans ce cas, il est probablement préférable d'utiliser chronique.
Jan-Philip Gehrcke