Question

Je souhaite rediriger stdout et stderr d'un processus vers un fichier unique. Comment puis-je faire cela dans Bash?

Était-ce utile?

La solution

Regardez ici . Devrait être:

yourcommand &>filename

(redirige stdout et stderr vers le nom du fichier).

Autres conseils

do_something 2>&1 | tee -a some_file

Ceci va rediriger stderr vers stdout et stdout vers un_fichier et pour l'imprimer sur stdout.

Vous pouvez rediriger stderr vers stdout et le stdout dans un fichier:

some_command >file.log 2>&1 

Voir http://tldp.org/LDP/abs/html/io -redirection.html

Ce format est préféré aux formats les plus populaires & amp; > format qui ne fonctionne que dans bash. Dans Bourne Shell, cela peut être interprété comme exécutant la commande en arrière-plan. Aussi le format est plus lisible 2 (est STDERR) redirigé vers 1 (STDOUT).

EDIT: modification de l'ordre comme indiqué dans les commentaires

# Close STDOUT file descriptor
exec 1<&-
# Close STDERR FD
exec 2<&-

# Open STDOUT as $LOG_FILE file for read and write.
exec 1<>$LOG_FILE

# Redirect STDERR to STDOUT
exec 2>&1

echo "This line will appear in $LOG_FILE, not 'on screen'"

Maintenant, un simple écho va écrire dans $ LOG_FILE. Utile pour démoniser.

À l'auteur du message d'origine,

Cela dépend de ce que vous devez accomplir. Si vous avez juste besoin de rediriger une commande que vous appelez à partir de votre script, les réponses sont déjà données. Le mien concerne la redirection dans du script en cours, qui affecte toutes les commandes / fonctions intégrées (y compris les fourches) après l'extrait de code mentionné.

Une autre solution intéressante consiste à rediriger simultanément std-err / out ET vers le consignateur ou le fichier journal, ce qui implique le fractionnement du "flux". Entre deux. Cette fonctionnalité est fournie par la commande 'tee' qui peut écrire / ajouter plusieurs descripteurs de fichier (fichiers, sockets, pipes, etc.) à la fois: tee FILE1 FILE2 ... > (cmd1) > (cmd2) ...

exec 3>&1 4>&2 1> >(tee >(logger -i -t 'my_script_tag') >&3) 2> >(tee >(logger -i -t 'my_script_tag') >&4)
trap 'cleanup' INT QUIT TERM EXIT


get_pids_of_ppid() {
    local ppid="$1"

    RETVAL=''
    local pids=`ps x -o pid,ppid | awk "\\$2 == \\"$ppid\\" { print \\$1 }"`
    RETVAL="$pids"
}


# Needed to kill processes running in background
cleanup() {
    local current_pid element
    local pids=( "$" )

    running_pids=("${pids[@]}")

    while :; do
        current_pid="${running_pids[0]}"
        [ -z "$current_pid" ] && break

        running_pids=("${running_pids[@]:1}")
        get_pids_of_ppid $current_pid
        local new_pids="$RETVAL"
        [ -z "$new_pids" ] && continue

        for element in $new_pids; do
            running_pids+=("$element")
            pids=("$element" "${pids[@]}")
        done
    done

    kill ${pids[@]} 2>/dev/null
}

Donc, depuis le début. Supposons que nous ayons un terminal connecté à / dev / stdout (FD # 1) et à / dev / stderr (FD # 2). En pratique, cela peut être un tuyau, une prise ou autre chose.

  • Créez les FD n ° 3 et n ° 4 et pointez sur le même "emplacement". comme n ° 1 et n ° 2 respectivement. Changer le FD n ° 1 n'affecte pas le FD n ° 3 à partir de maintenant. Maintenant, les FD n ° 3 et n ° 4 désignent respectivement STDOUT et STDERR. Ceux-ci seront utilisés comme réel terminaux STDOUT et STDERR.
  • 1 > > (...) redirige STDOUT vers une commande entre parenthèses
  • parens (sous-shell) exécute la commande 'tee' à partir de STDOUT (pipe) de l'exec et redirige vers la commande 'logger' via un autre canal vers un sous-shell dans les parens. Simultanément, il copie la même entrée sur le FD n ° 3 (terminal)
  • la deuxième partie, très similaire, traite de la même astuce pour STDERR et les FD n ° 2 et n ° 4.

Résultat de l'exécution d'un script comportant la ligne ci-dessus et, en outre, celle-ci:

echo "Will end up in STDOUT(terminal) and /var/log/messages"

... se présente comme suit:

$ ./my_script
Will end up in STDOUT(terminal) and /var/log/messages

$ tail -n1 /var/log/messages
Sep 23 15:54:03 wks056 my_script_tag[11644]: Will end up in STDOUT(terminal) and /var/log/messages

Si vous souhaitez obtenir une image plus nette, ajoutez ces 2 lignes au script:

ls -l /proc/self/fd/
ps xf
bash your_script.sh 1>file.log 2>&1

1 > fichier.log indique au shell d'envoyer STDOUT au fichier fichier.log , et 2 > & amp; 1 l'indique pour rediriger STDERR (descripteur de fichier 2) vers STDOUT (descripteur de fichier 1).

Remarque: L'ordre est important, comme l'a souligné liw.fi. 2 > & amp; 1 1 > file.log ne fonctionne pas.

Curieusement, cela fonctionne:

yourcommand &> filename

Mais cela donne une erreur de syntaxe:

yourcommand &>> filename
syntax error near unexpected token `>'

Vous devez utiliser:

yourcommand 1>> filename 2>&1

Réponse courte: Commande > nom de fichier 2 > & amp; 1 ou Commande & amp; > nom de fichier

Explication:

Considérez le code suivant qui affiche le mot "stdout". sur la sortie standard et le mot "stderror" à stderror.

$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror

Notez que le '& amp;' l'opérateur indique à bash que 2 est un descripteur de fichier (qui pointe vers le stderr) et non un nom de fichier. Si nous omettions le '& amp;', cette commande afficherait stdout sur stdout et créerait un fichier nommé "2". et écrivez stderror à cet endroit.

En expérimentant le code ci-dessus, vous pouvez voir par vous-même exactement comment fonctionnent les opérateurs de redirection. Par exemple, en modifiant le fichier qui des deux descripteurs 1,2 , est redirigé vers / dev / null , les deux lignes de code suivantes suppriment tout du stdout, et tout de stderror respectivement (en imprimant ce qui reste).

$ (echo "stdout"; echo "stderror" >&2) 1>/dev/null
stderror
$ (echo "stdout"; echo "stderror" >&2) 2>/dev/null
stdout

Nous pouvons maintenant expliquer pourquoi la solution explique pourquoi le code suivant ne produit aucune sortie:

(echo "stdout"; echo "stderror" >&2) >/dev/null 2>&1

Pour bien comprendre cela, je vous recommande vivement de lire ce document page Web sur les tableaux de descripteurs de fichiers . En supposant que vous ayez fait cette lecture, nous pouvons procéder. Notez que Bash traite de gauche à droite; Bash voit donc en premier & dev / null (identique à 1 > / dev / null ) et définit le descripteur de fichier 1 sur / dev / null au lieu de la sortie standard. Ceci fait, Bash se déplace alors vers la droite et voit 2 > & amp; 1 . Cela définit le descripteur de fichier 2 sur le même fichier que le descripteur de fichier 1 (et non sur le descripteur de fichier 1 lui-même !!!! (voir cette ressource sur les pointeurs pour plus d’informations)). Comme le descripteur de fichier 1 pointe sur / dev / null et que le descripteur de fichier 2 pointe sur le même fichier que le descripteur de fichier 1, le descripteur de fichier 2 pointe également sur / dev / null. Ainsi, les deux descripteurs de fichier pointent vers / dev / null, raison pour laquelle aucune sortie n'est rendue.

Pour vérifier si vous comprenez vraiment le concept, essayez de deviner la sortie lorsque nous inversons l'ordre de redirection:

(echo "stdout"; echo "stderror" >&2)  2>&1 >/dev/null
  

stderror

Le raisonnement est le suivant: si vous évaluez de gauche à droite, Bash voit 2 > & amp; 1 et définit donc le descripteur de fichier 2 pour qu'il pointe au même endroit que le descripteur de fichier 1, c’est-à-dire stdout. Il définit ensuite le descripteur de fichier 1 (rappelez-vous que > / dev / null = 1 > / dev / null) pour pointer sur > / dev / null, supprimant ainsi tout ce qui serait normalement envoyé à la sortie standard. Ainsi, il ne nous reste plus que ce qui n’a pas été envoyé à stdout dans le sous-shell (le code entre parenthèses) - c’est-à-dire "stderror".   La chose intéressante à noter est que même si 1 est juste un pointeur sur la sortie standard, le pointeur redirigé de 2 à 1 via 2 > & amp <1> / code> ne forme PAS une chaîne de pointeurs 2 - > 1 - > stdout. Si tel est le cas, le code 2 > & amp; 1 > / dev / null a pour résultat de rediriger 1 vers / dev / null, ce qui donnerait à la chaîne de pointeur 2 - > 1 - > / dev / null, et donc le code ne générerait rien, contrairement à ce que nous avons vu ci-dessus.

Enfin, je ferais remarquer qu’il existe un moyen plus simple de procéder:

À partir de la section 3.6.4, ici , nous pouvons utiliser l'opérateur & amp; > pour rediriger stdout et stderr. Ainsi, pour rediriger les sorties stderr et stdout de toute commande vers \ dev \ null (ce qui supprime la sortie), nous tapons simplement $ commande & amp; > / dev / null ou dans le cas de mon exemple:

$ (echo "stdout"; echo "stderror" >&2) &>/dev/null

Points clés:

  • Les descripteurs de fichier se comportent comme des pointeurs (bien que les descripteurs de fichier ne soient pas identiques aux pointeurs de fichier)
  • redirection d'un fi
LOG_FACILITY="local7.notice"
LOG_TOPIC="my-prog-name"
LOG_TOPIC_OUT="$LOG_TOPIC-out[$]"
LOG_TOPIC_ERR="$LOG_TOPIC-err[$]"

exec 3>&1 > >(tee -a /dev/fd/3 | logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_OUT" )
exec 2> >(logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_ERR" )

C'est lié: écrire stdOut & amp; stderr to syslog.

Cela fonctionne presque, mais pas de xinted; (

Je souhaitais une solution pour que la sortie de stdout plus stderr soit écrite dans un fichier journal et stderr toujours sur la console. Il me fallait donc dupliquer la sortie stderr via un tee.

Voici la solution que j'ai trouvée:

command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
  • Premiers échanges stderr et stdout
  • puis ajoutez la sortie standard au fichier journal
  • dirigez stderr vers tee et ajoutez-le également au fichier journal

Pour la situation, lorsque "tuyauterie" est nécessaire, vous pouvez utiliser:

  

| & amp;

Par exemple:

echo -ne "15\n100\n"|sort -c |& tee >sort_result.txt

ou

TIMEFORMAT=%R;for i in `seq 1 20` ; do time kubectl get pods |grep node >>js.log  ; done |& sort -h

Ces solutions basées sur bash peuvent diriger séparément STDOUT et STDERR (de STDERR à "trier" c ou "à trier" h.).

" Le plus facile " way (bash4 uniquement): ls * 2 > & amp; - 1 > & amp; - .

Les fonctions suivantes peuvent être utilisées pour automatiser le processus de basculement des sorties entre stdout / stderr et un fichier journal. Exemple d'utilisation à l'intérieur d'un script:

#!/bin/bash

    #set -x

    # global vars
    OUTPUTS_REDIRECTED="false"
    LOGFILE=/dev/stdout

    # "private" function used by redirect_outputs_to_logfile()
    function save_standard_outputs {
        if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
            exit 1;
        fi
        exec 3>&1
        exec 4>&2

        trap restore_standard_outputs EXIT
    }

    # Params: $1 => logfile to write to
    function redirect_outputs_to_logfile {
        if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
            exit 1;
        fi
        LOGFILE=$1
        if [ -z "$LOGFILE" ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"

        fi
        if [ ! -f $LOGFILE ]; then
            touch $LOGFILE
        fi
        if [ ! -f $LOGFILE ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
            exit 1
        fi

        save_standard_outputs

        exec 1>>${LOGFILE%.log}.log
        exec 2>&1
        OUTPUTS_REDIRECTED="true"
    }

    # "private" function used by save_standard_outputs() 
    function restore_standard_outputs {
        if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: Cannot restore standard outputs because they have NOT been redirected"
            exit 1;
        fi
        exec 1>&-   #closes FD 1 (logfile)
        exec 2>&-   #closes FD 2 (logfile)
        exec 2>&4   #restore stderr
        exec 1>&3   #restore stdout

        OUTPUTS_REDIRECTED="false"
    }

Pour tcsh, je dois utiliser la commande suivante:

command >& file

Si vous utilisez la commande , & amp; > fichier , il donnera la " commande null invalide " erreur.

@ fernando-fabreti

En ajoutant à ce que vous avez fait, j'ai légèrement modifié les fonctions et supprimé la & amp; - fermeture, et cela a fonctionné pour moi.

    function saveStandardOutputs {
      if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
        exec 3>&1
        exec 4>&2
        trap restoreStandardOutputs EXIT
      else
          echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
          exit 1;
      fi
  }

  # Params: $1 => logfile to write to
  function redirectOutputsToLogfile {
      if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
        LOGFILE=$1
        if [ -z "$LOGFILE" ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
        fi
        if [ ! -f $LOGFILE ]; then
            touch $LOGFILE
        fi
        if [ ! -f $LOGFILE ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
            exit 1
        fi
        saveStandardOutputs
        exec 1>>${LOGFILE}
        exec 2>&1
        OUTPUTS_REDIRECTED="true"
      else
        echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
          exit 1;
      fi
  }
  function restoreStandardOutputs {
      if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
      exec 1>&3   #restore stdout
      exec 2>&4   #restore stderr
      OUTPUTS_REDIRECTED="false"
     fi
  }
  LOGFILE_NAME="tmp/one.log"
  OUTPUTS_REDIRECTED="false"

  echo "this goes to stdout"
  redirectOutputsToLogfile $LOGFILE_NAME
  echo "this goes to logfile"
  echo "${LOGFILE_NAME}"
  restoreStandardOutputs 
  echo "After restore this goes to stdout"

Dans les cas où vous envisagez d'utiliser des éléments tels que exec 2 > & amp; 1 , il est plus facile de lire si possible en réécrivant le code à l'aide de fonctions bash telles que celle-ci:

function myfunc(){
  [...]
}

myfunc &>mylog.log
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top