Capture de stdout et de stderr dans Bash [dupliquer]

cette question a déjà une réponse ici:

  • Capture stdout et stderr en différentes variables 13 Réponses

je connais cette syntaxe

var=`myscript.sh`

ou

var=$(myscript.sh)

capturera le résultat ( stdout ) de myscript.sh en var . Je pourrais rediriger stderr dans stdout si je voulais capturer les deux. Comment sauvegarder chacun d'eux pour séparer les variables?

mon cas d'utilisation ici est si le code de retour est nonzero je veux faire écho stderr et supprimer autrement. Il peut y avoir d'autres façons de le faire, mais cette approche semble fonctionner, si c'est réellement possible.

27
demandé sur codeforester 2012-12-10 22:08:21

6 réponses

il n'y a aucun moyen de capturer les deux sans fichier temp.

vous pouvez capturer stderr à variable et passer stdout à l'écran de l'utilisateur (échantillon de ici ):

exec 3>&1                    # Save the place that stdout (1) points to.
output=$(command 2>&1 1>&3)  # Run command.  stderr is captured.
exec 3>&-                    # Close FD #3.

# Or this alternative, which captures stderr, letting stdout through:
{ output=$(command 2>&1 1>&3-) ;} 3>&1

mais il n'y a aucun moyen de capturer à la fois stdout et stderr:

ce que vous ne pouvez pas faire est de capturer stdout dans une variable, et stderr dans une autre, en utilisant seulement des redirections FD. Vous doit utilisation d'un fichier temporaire (ou un tube nommé) à atteindre.

23
répondu zb' 2015-06-16 21:21:51

il y a une façon vraiment laide de capturer stderr et stdout dans deux variables séparées sans fichiers temporaires (si vous aimez la plomberie), en utilisant substitution de processus , source , et declare de manière appropriée. Je vais appeler votre commande banana . Vous pouvez imiter une telle commande avec une fonction:

banana() {
    echo "banana to stdout"
    echo >&2 "banana to stderr"
}

je suppose que vous voulez la sortie standard de banana dans la variable bout et l'erreur standard de banana dans la variable berr . Voici la magie qui permettra cela (Bash≥4 seulement):

. <({ berr=$({ bout=$(banana); } 2>&1; declare -p bout >&2); declare -p berr; } 2>&1)

alors, que se passe-t-il ici?

commençons par le terme le plus proche:

bout=$(banana)

c'est juste la façon standard d'attribuer à bout la sortie standard de banana , l'erreur standard étant affichée sur votre terminal.

alors:

{ bout=$(banana); } 2>&1

assignera toujours à bout le stdout de banana , mais le stderr de banana est affiché sur le terminal via stdout (merci à la redirection 2>&1 .

puis:

{ bout=$(banana); } 2>&1; declare -p bout >&2

fera comme ci-dessus, mais affichera également sur le terminal (via stderr) le contenu de bout avec le declare builtin: celui-ci sera réutilisé prochainement.

alors:

berr=$({ bout=$(banana); } 2>&1; declare -p bout >&2); declare -p berr

assignera à berr le stderr de banana et affichera le contenu de berr avec declare .

à ce point, vous aurez sur votre écran terminal:

declare -- bout="banana to stdout"
declare -- berr="banana to stderr"

avec la ligne

declare -- bout="banana to stdout"

affiché via stderr.

une redirection finale:

{ berr=$({ bout=$(banana); } 2>&1; declare -p bout >&2); declare -p berr; } 2>&1

aura le précédent affiché sur la sortie standard stdout.

enfin, nous utilisons un substitution de procédé à source le contenu de ces lignes.


vous avez aussi mentionné le code de retour de la commande. Remplacer banana par:

banana() {
    echo "banana to stdout"
    echo >&2 "banana to stderr"
    return 42
}

nous aurons aussi le code de retour de banana dans la variable bret comme ainsi:

. <({ berr=$({ bout=$(banana); bret=$?; } 2>&1; declare -p bout bret >&2); declare -p berr; } 2>&1)

vous pouvez faire sans sourcing et une substitution de processus en utilisant eval aussi (et il fonctionne avec Bash<4 aussi):

eval "$({ berr=$({ bout=$(banana); bret=$?; } 2>&1; declare -p bout bret >&2); declare -p berr; } 2>&1)"

et tout cela est sûr, parce que la seule chose que nous sommes source ing ou eval ing sont obtenus à partir de declare -p et sera toujours correctement échappé.


bien sûr, si vous voulez la sortie dans un tableau (par exemple, avec mapfile , si vous utilisez Bash≥4-sinon remplacer mapfile par while - read boucle), l'adaptation est simple.

par exemple:

banana() {
    printf 'banana to stdout %d\n' {1..10}
    echo >&2 'banana to stderr'
    return 42
}

. <({ berr=$({ mapfile -t bout < <(banana); } 2>&1; declare -p bout >&2); declare -p berr; } 2>&1)

et avec le code de retour:

. <({ berr=$({ mapfile -t bout< <(banana; bret=$?; declare -p bret >&3); } 3>&2 2>&1; declare -p bout >&2); declare -p berr; } 2>&1)
30
répondu gniourf_gniourf 2016-04-23 20:32:58

Vous pouvez le faire:

OUT=$(myscript.sh 2> errFile)
ERR=$(<errFile)

maintenant $OUT aura la sortie standard de votre script et $ERR aura la sortie d'erreur de votre script.

13
répondu anubhava 2012-12-10 18:12:16

un moyen facile, mais pas élégant: rediriger stderr vers un fichier temporaire et puis le relire:

TMP=$(mktemp)
var=$(myscript.sh 2> "$TMP")
err=$(cat "$TMP")
rm "$TMP"
5
répondu jofel 2012-12-10 18:15:33

bien que je n'ai pas trouvé un moyen de capturer stderr et stdout pour séparer les variables dans bash, j'envoie les deux à la même variable avec...

result=$( { grep "JUNK" ./junk.txt; } 2>&1 )

... puis je vérifie le statut de sortie"$?"et agir de manière appropriée sur les données dans $result.

3
répondu jack 2014-01-09 20:09:54
# NAME
#   capture - capture the stdout and stderr output of a command
# SYNOPSIS
#   capture <result> <error> <command>
# DESCRIPTION
#   This shell function captures the stdout and stderr output of <command> in
#   the shell variables <result> and <error>.
# ARGUMENTS
#   <result>  - the name of the shell variable to capture stdout
#   <error>   - the name of the shell variable to capture stderr
#   <command> - the command to execute
# ENVIRONMENT
#   The following variables are mdified in the caller's context:
#    - <result>
#    - <error>
# RESULT
#   Retuns the exit code of <command>.
# SOURCE
capture ()
{
    # Name of shell variable to capture the stdout of command.
    result=
    shift

    # Name of shell variable to capture the stderr of command.
    error=
    shift

    # Local AWK program to extract the error, the result, and the exit code
    # parts of the captured output of command.
    local evaloutput='
        {
            output [NR] = "151900920"
        }
        END \
        {
            firstresultline = NR - output [NR - 1] - 1
            if (Var == "error") \
            {
                for (i = 1; i < firstresultline; ++ i)
                {
                    printf ("%s\n", output [i])
                }
            }
            else if (Var == "result") \
            {
                for (i = firstresultline; i < NR - 1; ++ i)
                {
                    printf ("%s\n", output [i])
                }
            }
            else \
            {
                printf ("%d", output [NR])
            }
        }'

    # Capture the stderr and stdout output of command, as well as its exit code.
    local output="$(
    {
        local stdout
        stdout="$($*)"
        local exitcode=$?
        printf "\n%s\n%d\n%d\n" \
               "$stdout" "$(echo "$stdout" | wc -l)" "$exitcode"
    } 2>&1)"

    # extract the stderr, the stdout, and the exit code parts of the captured
    # output of command.
    printf -v $error "%s" \
                     "$(echo "$output" | gawk -v Var="error" "$evaloutput")"
    printf -v $result "%s" \
                      "$(echo "$output" | gawk -v Var="result" "$evaloutput")"
    return $(echo "$output" | gawk "$evaloutput")
}
-1
répondu Tom Michaelis 2015-02-16 11:23:01