Masquer la sortie d'une commande shell uniquement en cas de succès?

12

Masquer la sortie d'une commande shell implique généralement de rediriger stderr et stdout. Existe-t-il une fonction ou une commande intégrée qui, par défaut, masque la sortie, mais en cas d'erreur, vide toute la sortie accumulée? Je voudrais l'exécuter comme un wrapper pour les sshcommandes à distance . Maintenant, je les utilise en redirection, mais je ne sais pas ce qui les a fait échouer, et ils sont tout simplement trop verbeux.

EDIT: Au final, j'ai créé le modèle suivant basé sur la réponse de @Belmin que j'ai légèrement modifié pour accumuler toutes les commandes précédentes du script, utiliser l'identifiant de processus actuel, supprimer automatiquement le journal et ajouter une erreur rouge d'erreur message en cas de problème. Dans ce modèle, les silentwrappers initiaux réussiront, puis échouera la troisième commande car le répertoire existe déjà:

#!/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
la source
2
Si vous redirigez uniquement stdout, stderr apparaîtra toujours; est-ce suffisant pour vous, ou voulez-vous également voir stdout en cas d'erreur?
Kromey
Je veux voir les deux mais seulement si quelque chose ne va pas, sinon je ne veux rien voir.
Grzegorz Adam Hankiewicz
2
Ce que je fais, c'est imprimer stdout & stderr dans un fichier journal pour qu'il n'encombre pas l'écran. J'imprime également stderr à l'écran, donc s'il est et erreur que je peux le voir. Si j'ai besoin de détails, je peux vérifier le fichier journal, qui contient la sortie du programme et les erreurs du programme en contexte. De cette façon, je peux «voir les deux mais seulement si quelque chose ne va pas». est-ce que cela aide? Voir stackoverflow.com/questions/2871233/…
Stefan Lasiewski
1
Est-il sûr de rediriger stderr et stdout vers le même fichier avec deux redirections distinctes? Je pensais que nous devrions toujours utiliser 2>&1quelque chose comme:$* >>"${SILENT_LOG}" 2>&1" || report_and_exit
psmith

Réponses:

3

Je configurerais une fonction bash comme ceci:

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

Ensuite, vous pouvez simplement exécuter la commande:

suppress foo -a bar
Belmin Fernandez
la source
Un attaquant disposant d'un accès non root sur votre système pourrait essayer de créer un lien symbolique entre rm et l'appel de commande, qui pointerait vers /etc/passswdun autre fichier critique et ferait écraser le contenu.
Mitar
1
BTW, l'ordre des redirections ci-dessus devrait être: $* > /tmp/surpress.out 2>&1Cela capture vraiment le stderr.
Mitar
2
$*n'est pas le meilleur pour gérer les entrées arbitraires. Surtout quand il contient des espaces ou des drapeaux. Le plus portable est ${1+"$@"}selon stackoverflow.com/questions/743454/…
balrok
Modifié par les deux commentaires. Merci --- bonne info.
Belmin Fernandez
7

Il devrait être assez facile d'écrire un script à cet effet.

Quelque chose comme ce script complètement non testé.

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

D'un autre côté, pour les commandes que j'exécute dans le cadre d'un script, je veux généralement quelque chose de mieux que d'imprimer simplement toutes les sorties. Je limite souvent ce que je vois à l'inconnu. Voici un script que j'ai adapté de quelque chose que j'ai lu il y a plus de dix ans.

#!/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
la source
5

Je ne pense pas qu'il existe une façon propre de le faire, la seule chose à laquelle je peux penser est

  • Capturez la sortie de la commande.
  • Vérifiez la valeur de retour de la commande et si elle a échoué
    • afficher la sortie capturée.

La mise en œuvre de ceci pourrait cependant être un projet intéressant mais peut-être au-delà des questions / réponses.

user9517
la source
Devrait être faisable avec une fonction. Hm, laissez-moi essayer.
Belmin Fernandez
1

aller court avec quelque chose comme tehcommand &>/tmp/$$ || cat /tmp/$$

dépend de la facilité d'utilisation / de la frappe que vous voulez / besoin. (par exemple en l'utilisant comme pipe ou en passant la commande par argument)

Le script court @zoredache est fondamentalement un proto-wrapper pour cela, ce qui donnerait plus de robustesse, gérerait la concurrence, etc.

rogerovo
la source
1

Essayez donc:

out=`command args...` || echo $out
user2743554
la source
2
Je l' out="$(command args...)" || echo "$out"
écrirais