Saisir stdout et stderr dans différentes variables

est-il possible de stocker ou de capturer stdout et stderr dans variables différentes , sans utiliser de fichier temp? En ce moment je fais ceci pour obtenir stdout dans out et stderr dans err en tournant some_command , mais j'ai comme pour éviter le fichier temp.

error_file=$(mktemp)
out=$(some_command 2>$error_file)
err=$(< error_file)
rm $error_file
50
demandé sur codeforester 2012-06-14 10:19:07

13 réponses

Ok, c'est un peu moche, mais voici une solution:

unset t_std t_err
eval "$( (echo std; echo err >&2) \
        2> >(readarray -t t_err; typeset -p t_err) \
         > >(readarray -t t_std; typeset -p t_std) )"

(echo std; echo err >&2) doit être remplacé par la commande effective. La sortie de stdout est sauvegardée dans le tableau $t_std ligne par ligne omettant les nouvelles lignes (le -t ) et stderr dans $t_err .

si vous n'aimez pas les tableaux, vous pouvez le faire

unset t_std t_err
eval "$( (echo std; echo err >&2 ) \
        2> >(t_err=$(cat); typeset -p t_err) \
         > >(t_std=$(cat); typeset -p t_std) )"

qui à peu près imite le comportement de var=$(cmd) sauf pour la valeur de $? qui nous amène à la dernière modification:

unset t_std t_err t_ret
eval "$( (echo std; echo err >&2; exit 2 ) \
        2> >(t_err=$(cat); typeset -p t_err) \
         > >(t_std=$(cat); typeset -p t_std); t_ret=$?; typeset -p t_ret )"

ici $? est conservé dans $t_ret

testé sur Debian wheezy en utilisant GNU bash , Version 4.2.37(1)-release (i486-pc-linux-gnu) .

31
répondu TheConstructor 2017-05-22 14:22:39
"151980920 Jonathan" a la réponse . Pour référence, c'est le ksh93 truc. (nécessite une version non ancienne).

function out {
    echo stdout
    echo stderr >&2
}

x=${ { y=$(out); } 2>&1; }
typeset -p x y # Show the values

produit

x=stderr
y=stdout

la syntaxe ${ cmds;} est juste une substitution de commande qui ne crée pas de sous-couche. Les commandes sont exécutées dans l'environnement shell actuel. L'espace au début est important ( { est un mot réservé).

le groupe de commande interne est redirigé vers stdout (de sorte qu'il s'applique à la substitution interne). Ensuite, le stdout de out est affecté à y , et le stderr redirigé est capturé par x , sans la perte habituelle de y à un subshell de substitution de commande.

il n'est pas possible dans d'autres coquilles, parce que toutes les constructions qui capturent la production exigent de mettre le producteur dans un sous-puits, qui dans ce cas, inclurait la cession.

mise à jour: Désormais également pris en charge par mksh.

14
répondu ormaaj 2017-05-23 11:47:32

cette commande définit les valeurs stdout (stdval) et stderr (errval) dans l'interpréteur de commandes actuel:

eval "$( execcommand 2> >(setval errval) > >(setval stdval); )"

à condition que cette fonction ait été définie:

function setval { printf -v "" "%s" "$(cat)"; declare -p ""; }

Changement execcommand à la capture de commande "ls", "cp", "df", etc.


tout cela est basé sur l'idée que nous pourrions convertir toutes les valeurs capturées en une ligne de texte à l'aide de la fonction setval, puis setval est utilisé pour capturer chaque valeur dans cette structure:

execcommand 2> CaptureErr > CaptureOut

Convertissez chaque valeur de capture en un appel setval:

execcommand 2> >(setval errval) > >(setval stdval)

tout Envelopper à l'intérieur d'un appel d'exécution et l'écho:

echo "$( execcommand 2> >(setval errval) > >(setval stdval) )"

vous obtiendrez les appels declares que chaque setval crée:

declare -- stdval="I'm std"
declare -- errval="I'm err"

pour exécuter ce code (et obtenir le jeu vars) utilisez eval:

eval "$( execcommand 2> >(setval errval) > >(setval stdval) )"

et enfin echo l'ensemble des revendeurs:

echo "std out is : |$stdval| std err is : |$errval|

il est également possible d'inclure la valeur return (exit).

Un exemple complet de script bash ressemble à ceci:

#!/bin/bash --

# The only function to declare:
function setval { printf -v "" "%s" "$(cat)"; declare -p ""; }

# a dummy function with some example values:
function dummy { echo "I'm std"; echo "I'm err" >&2; return 34; }

# Running a command to capture all values
#      change execcommand to dummy or any other command to test.
eval "$( dummy 2> >(setval errval) > >(setval stdval); <<<"$?" setval retval; )"

echo "std out is : |$stdval| std err is : |$errval| return val is : |$retval|"
13
répondu 2016-08-09 04:01:56

À somme tout pour le bénéfice du lecteur, voici un

Facile À Réutiliser bash Solution

cette version utilise des subshells et fonctionne sans tempfile s. (Pour une version tempfile qui fonctionne sans subshells, voir mon autre réponse .)

: catch STDOUT STDERR cmd args..
catch()
{
eval "$({
__2="$(
  { __1="$("${@:3}")"; } 2>&1;
  ret=$?;
  printf '%q=%q\n' "" "$__1" >&2;
  exit $ret
  )"
ret="$?";
printf '%s=%q\n' "" "$__2" >&2;
printf '( exit %q )' "$ret" >&2;
} 2>&1 )";
}

exemple d'utilisation:

dummy()
{
echo "" >&2
echo "" >&1
return ""
}

catch stdout stderr dummy 3 $'\ndiffcult\n data \n\n\n' $'\nother\n difficult \n  data  \n\n'

printf 'ret=%q\n' "$?"
printf 'stdout=%q\n' "$stdout"
printf 'stderr=%q\n' "$stderr"

l'impression

ret=3
stdout=$'\ndiffcult\n data '
stderr=$'\nother\n difficult \n  data  '

pour qu'il puisse être utilisé sans réflexion approfondie. Il suffit de mettre catch VAR1 VAR2 devant n'importe quel command args.. et vous avez terminé.

certains if cmd args..; then deviendront if catch VAR1 VAR2 cmd args..; then . Vraiment rien de complexe.

Discussion

Q: Comment ça marche?

Il encapsule les idées des autres réponses ici dans un fonction, de sorte qu'il peut facilement être réutilisé.

catch() utilise essentiellement eval pour définir les deux variables. C'est similaire à https://stackoverflow.com/a/18086548

Envisager un appel de catch out err dummy 1 2a 3b :

  • laissons tomber le eval "$({ et le __2="$( pour l'instant. Je viendrai plus tard.

  • __1="$("$("${@:3}")"; } 2>&1; exécute dummy 1 2 3 et stocke stdout dans __1 pour une utilisation ultérieure. Ainsi __1 devient 2a . Il redirige également stderr de dummy à stdout , de sorte que la prise extérieure peut recueillir stdout

  • ret=$?; capture le code de sortie, qui est 1

  • printf '%q=%q\n' "" "$__1" >&2; puis sorties out=2a à stderr . stderr est utilisé ici, car l'actuel stdout a déjà repris le rôle de stderr de la commande dummy .

  • exit $ret transmet ensuite le code de sortie ( 1 ) à l'étape suivante.

Maintenant à l'extérieur __2="$( ... )" :

15191360920 15191370920 15191040920 ce qui précède, qui est le stderr du dummy appel, dans la variable __2 . (Nous pourrions réutiliser __1 ici, mais j'ai utilisé __2 pour le rendre moins déroutant.). Ainsi __2 devient 3b

  • ret="$?"; capture le code de retour 1 (de dummy ) à nouveau

  • printf '%s=%q\n' "" "$__2" >&2; puis sorties err=3a à stderr . stderr est de nouveau utilisé, car il était déjà utilisé pour produire l'autre variable out=2a .

  • printf '( exit %q )' "$ret" >&2; then outputs the code to set the proper return value. I did not find a better way, as assignig it to a variable needs a variable name, which then cannot be used as first oder second argument to attraper".

  • s'il vous Plaît noter que, comme l'optimisation, nous aurions pu écrire ces 2 printf comme un seul, comme printf '%s=%q\n( exit %q ) "$__2" "$ret".

    alors qu'avons-nous jusqu'à présent?

    nous avons

    out=2a
    err=3b
    ( exit 1 )
    

    out est de , 2a est de stdout de dummy , err est de , 3b est de stderr de dummy , et le 1 est du code de retour de dummy .

    s'il vous Plaît noter que %q dans le format de printf prend soin de citer, par exemple, que le shell voit bon (unique) des arguments quand il s'agit de eval . 2a et 3b sont si simples, qu'ils sont copiés littéralement.

    Maintenant à l'extérieur eval "$({ ... } 2>&1 )"; :

    exécute tous les au-dessus de qui sortent les 2 variables et le exit , le saisit (à cet effet le 2>&1 ) et le divise dans la coquille courante en utilisant eval .

    de cette façon les 2 variables sont définies et le code de retour est bien.

    Q: il utilise eval qui est le mal. Donc, est-il sûr?

    • aussi longtemps que printf %q n'a pas de bogues, il doit être sûr. Mais tu dois toujours être très prudent, pense à ShellShock.

    Q: Bugs?

    • Aucun bogue évident n'est connu, sauf le suivant:

      • il a besoin d'une grande mémoire et D'un processeur, car tout va dans les variables et doit être analysé à l'envers par le shell. Afin de l'utiliser à bon escient.
      • Comme d'habitude $(echo $'\n\n\n\n') avale tous les sauts de ligne , pas seulement le dernier. C'est une POSIX exigence. Si vous avez besoin d'obtenir le LFs indemne, il suffit d'ajouter un certain caractère de fuite à la sortie et de le supprimer après comme dans la recette suivante (regardez le x de fuite qui permet de lire un softlink pointant vers un fichier qui se termine par un $'\n' ):

        target="$(readlink -e "$file")x"
        target="${target%x}"
        
      • Shell-variables ne peuvent pas transporter de l'octet NUL ( $'"1519840920"' ). Ils sont tout simplement ignorés si elles se produisent dans stdout ou stderr .

    • la commande donnée s'exécute dans un sous-puits. Il n'a donc pas accès à $PPID , et ne peut pas non plus modifier les variables shell. Vous pouvez catch une fonction shell, même les builtins, mais ceux-ci ne pourront pas modifier les variables shell (car tout ce qui tourne dans $( .. ) ne peut pas faire cela). Donc si vous avez besoin d'exécuter une fonction dans la shell courante et de l'attraper c'est stderr/stdout, vous devez le faire de la manière habituelle avec tempfile s. (Il y a des moyens de le faire, de sorte que l'interruption de la coque ne laisse pas de débris derrière, mais c'est complexe et mérite sa propre réponse.)

    Q: Version Bash?

    • je pense que vous avez besoin de Bash 4 et plus (en raison de printf %q )

    Q: cela semble toujours si maladroit.

    • à Droite. une Autre réponse ici montre comment il peut être fait dans ksh beaucoup plus proprement. Cependant je ne suis pas habitué à ksh , donc je laisse à d'autres de créer une recette similaire facile à réutiliser pour ksh .

    Q: Pourquoi ne pas utiliser ksh alors?

    • parce qu'il s'agit d'une bash solution

    Q: le script peut être amélioré

    • bien sûr, vous pouvez extraire quelques octets et créer une solution plus petite ou plus incompréhensible. Aller juste pour elle ;)

    Q: Il y a une faute de frappe. : catch STDOUT STDERR cmd args.. doit se lire # catch STDOUT STDERR cmd args..

    • en fait, c'est ce qui est prévu. : apparaît dans bash -x tandis que les commentaires sont avalés en silence. Vous pouvez donc voir où se trouve l'analyseur si vous avez une erreur typographique dans la définition de la fonction. C'est un vieux de débogage truc. Mais attention, vous pouvez facilement créer quelques effets secondaires nets dans les arguments de : .

    Edit: Ajout d'un couple de plus ; il est plus facile de créer une doublure simple à partir de catch() . Et ajouté la section Comment ça marche.

    11
    répondu Tino 2017-05-23 12:26:38

    techniquement, les pipes nommées ne sont pas des fichiers temporaires et personne ici ne les mentionne. Ils stockent rien dans le système de fichiers et vous pouvez les supprimer dès que vous vous connectez (vous ne verrez pas):

    #!/bin/bash -e
    
    foo () {
        echo stdout1
        echo stderr1 >&2
        sleep 1
        echo stdout2
        echo stderr2 >&2
    }
    
    rm -f stdout stderr
    mkfifo stdout stderr
    foo >stdout 2>stderr &             # blocks until reader is connected
    exec {fdout}<stdout {fderr}<stderr # unblocks `foo &`
    rm stdout stderr                   # filesystem objects are no longer needed
    
    stdout=$(cat <&$fdout)
    stderr=$(cat <&$fderr)
    
    echo $stdout
    echo $stderr
    
    exec {fdout}<&- {fderr}<&- # free file descriptors, optional
    

    vous pouvez avoir plusieurs processus de fond de cette façon et recueillir asynchrone leurs stdouts et stderrs à un moment convenable, etc.

    si vous avez besoin de cela pour un seul processus, vous pouvez tout aussi bien utiliser des numéros FD codés en dur comme 3 et 4, au lieu de la syntaxe {fdout}/{fderr} (qui trouve un fd libre pour vous).

    6
    répondu Irfy 2015-10-16 09:06:33

    N'a pas aimé l'eval, donc voici une solution qui utilise quelques astuces de redirection pour capturer la sortie du programme vers une variable et ensuite parse cette variable pour extraire les différents composants. Le drapeau-w définit la taille du morceau et influence l'ordre des messages std-out/err dans le format intermédiaire. 1 donne une résolution potentiellement élevée au prix de frais généraux.

    #######                                                                                                                                                                                                                          
    # runs "$@" and outputs both stdout and stderr on stdin, both in a prefixed format allowing both std in and out to be separately stored in variables later.                                                                  
    # limitations: Bash does not allow null to be returned from subshells, limiting the usefullness of applying this function to commands with null in the output.                                                                   
    # example:                                                                                                                                                                                                                       
    #  var=$(keepBoth ls . notHere)                                                                                                                                                                                                  
    #  echo ls had the exit code "$(extractOne r "$var")"                                                                                                                                                                            
    #  echo ls had the stdErr of "$(extractOne e "$var")"                                                                                                                                                                            
    #  echo ls had the stdOut of "$(extractOne o "$var")"                                                                                                                                                                            
    keepBoth() {                                                                                                                                                                                                                     
      (                                                                                                                                                                                                                              
        prefix(){                                                                                                                                                                                                                    
          ( set -o pipefail                                                                                                                                                                                                          
            base64 -w 1 - | (                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
              while read c                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
              do echo -E "" "$c"                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
              done                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
            )                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
          )                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
        }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
        ( (                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
            "$@" | prefix o >&3                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
            echo  ${PIPESTATUS[0]} | prefix r >&3                                                                                                                                                                                                                                                                                                                                                                                                                                                           
          ) 2>&1 | prefix e >&1                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        ) 3>&1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
      )                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
    }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
    
    extractOne() { # extract                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
      echo "" | grep "^" | cut --delimiter=' ' --fields=2 | base64 --decode -                                                                                                                                                                                                                                                                                                                                                                                                                           
    }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
    
    3
    répondu mncl 2018-01-08 10:41:34

    pour résumer, je crois que la réponse est "non". La capture $( ... ) ne capture que la sortie standard vers la variable; il n'y a aucun moyen de capturer l'erreur standard dans une variable séparée. Donc, ce que vous avez est aussi net que possible.

    2
    répondu Jonathan Leffler 2012-06-14 06:33:42

    et alors?.. = D

    GET_STDERR=""
    GET_STDOUT=""
    get_stderr_stdout() {
        GET_STDERR=""
        GET_STDOUT=""
        unset t_std t_err
        eval "$( (eval ) 2> >(t_err=$(cat); typeset -p t_err) > >(t_std=$(cat); typeset -p t_std) )"
        GET_STDERR=$t_err
        GET_STDOUT=$t_std
    }
    
    get_stderr_stdout "command"
    echo "$GET_STDERR"
    echo "$GET_STDOUT"
    
    2
    répondu Eduardo Lucio 2015-12-10 17:04:52

    pour le bénéfice du lecteur voici une solution utilisant tempfile s.

    la question n'était pas d'utiliser tempfile S. Cependant cela pourrait être dû à la pollution indésirable de /tmp/ avec tempfile dans le cas où le shell meurt. Dans le cas de kill -9 certains trap 'rm "$tmpfile1" "$tmpfile2"' 0 ne tire pas.

    si vous êtes dans une situation où vous pouvez utiliser tempfile , mais que vous voulez ne jamais laisser de débris derrière vous , voici une recette.

    encore une fois, il est appelé catch() (comme mon autre réponse ) et a la même syntaxe d'appel:

    catch stdout stderr command args..

    # Wrappers to avoid polluting the current shell's environment with variables
    
    : catch_read returncode FD variable
    catch_read()
    {
    eval "=\"\`cat <&\`\"";
    # You can use read instead to skip some fork()s.
    # However read stops at the first NUL byte,
    # also does no \n removal and needs bash 3 or above:
    #IFS='' read -ru -d '' "";
    return ;
    }
    : catch_1 tempfile variable comand args..
    catch_1()
    {
    {
    rm -f "";
    "${@:3}" 66<&-;
    catch_read $? 66 "";
    } 2>&1 >"" 66<"";
    }
    
    : catch stdout stderr command args..
    catch()
    {
    catch_1 "`tempfile`" "${2:-stderr}" catch_1 "`tempfile`" "${1:-stdout}" "${@:3}";
    }
    

    Ce qu'il fait:

    • il crée deux tempfile s pour stdout et stderr . Cependant il enlève presque immédiatement ceux-ci, de sorte qu'ils ne sont autour pour un très court temps.

    • catch_1() capture stdout (FD 1) dans une variable et déplace stderr à stdout , de sorte que le suivant ("gauche") catch_1 peut attraper cela.

    • la transformation en catch se fait de droite à gauche, donc la gauche catch_1 est exécutée en dernier et les captures stderr .

    le pire qui puisse arriver est, que certains fichiers temporaires apparaissent sur /tmp/ , mais ils sont toujours vides dans ce cas. (Ils sont enlevés avant qu'ils ne se remplissent.). Généralement, cela ne devrait pas être un problème, car sous Linux tmpfs prend en charge environ 128K fichiers par Go de mémoire principale.

    • la commande donnée peut accéder à toutes les variables shell locales et les modifier. Ainsi, vous pouvez appeler une fonction shell qui a des effets secondaires!

    • This seulement deux bifurcations pour l'appel tempfile .

    "1519310920 des" Bugs":

    • absence de bonne manipulation des erreurs en cas de défaillance tempfile .

    • Cela fait l'enlèvement habituel \n de la coque. Voir le commentaire dans catch_read() .

    • vous ne pouvez pas utiliser le descripteur de fichier 66 pour transférer les données à votre commande. Si vous avez besoin de cela, utilisez un autre descripteur pour la redirection, comme 42 (notez que les shells très anciens n'offrent que des FDs jusqu'à 9).

    • cela ne peut pas traiter les octets NUL ( $'"1519270920"' ) dans stdout et stderr . (NUL est juste ignoré. Pour la variante read tout derrière un NUL est ignoré.)

    pour information:

    • Unix nous permet d'accéder les fichiers supprimés, tant que vous gardez une référence à eux autour (comme un filehandle ouvert). De cette façon, nous pouvons ouvrir et puis les enlever.
    2
    répondu Tino 2017-05-23 12:02:48

    si la commande 1) pas d'effets secondaires stateful et 2) est computationally bon marché, la solution la plus simple est de l'exécuter deux fois. J'ai principalement utilisé ceci pour le code qui s'exécute pendant la séquence de démarrage quand vous ne savez pas encore si le disque va fonctionner. Dans mon cas, il s'agissait d'un petit some_command donc il n'y avait pas de succès de performance pour courir deux fois, et la commande n'a pas eu d'effets secondaires.

    Le principal avantage est que c'est propre et facile à lire. Les solutions ici sont très intelligent, mais je n'aimerais pas être celui qui doit maintenir un script contenant le plus compliqué des solutions. Je recommanderais l'approche simple run-it-two si votre scénario fonctionne avec cela, car il est beaucoup plus propre et plus facile à entretenir.

    exemple:

    output=$(getopt -o '' -l test: -- "$@")
    errout=$(getopt -o '' -l test: -- "$@" 2>&1 >/dev/null)
    if [[ -n "$errout" ]]; then
            echo "Option Error: $errout"
    fi
    

    encore une fois, c'est seulement ok à faire parce que getopt n'a pas d'effets secondaires. Je sais que c'est sans danger parce que mon code parent appelle ça moins de 100 fois pendant tout le programme., et l'utilisateur ne remarquera jamais 100 appels getopt vs 200 appels getopt.

    0
    répondu Hamy 2015-03-02 04:14:58

    voici une variante plus simple qui n'est pas tout à fait ce que l'OP voulait, mais qui ne ressemble à aucune des autres options. Vous pouvez obtenir ce que vous voulez en réorganisant les descripteurs de fichier.

    le Test de la commande:

    %> cat xx.sh  
    #!/bin/bash
    echo stdout
    >&2 echo stderr
    

    qui à lui seul fait:

    %> ./xx.sh
    stdout
    stderr
    

    maintenant, Imprimez stdout, capturez stderr à une variable, & log stdout à un fichier

    %> export err=$(./xx.sh 3>&1 1>&2 2>&3 >"out")
    stdout
    %> cat out    
    stdout
    %> echo
    $err 
    stderr
    

    ou log stdout & capture stderr à une variable:

    export err=$(./xx.sh 3>&1 1>out 2>&3 )
    %> cat out
    stdout
    %> echo $err
    stderr
    

    vous comprenez.

    0
    répondu Bruce Edge 2016-03-05 01:28:25

    une solution de contournement, qui est hacky mais peut-être plus intuitive que certaines des suggestions sur cette page, est d'étiqueter les flux de sortie, de les fusionner, et de se diviser ensuite sur la base des tags. Par exemple, nous pourrions étiqueter stdout avec un préfixe "STDOUT":

    function someCmd {
        echo "I am stdout"
        echo "I am stderr" 1>&2
    }
    
    ALL=$({ someCmd | sed -e 's/^/STDOUT/g'; } 2>&1)
    OUT=$(echo "$ALL" | grep    "^STDOUT" | sed -e 's/^STDOUT//g')
    ERR=$(echo "$ALL" | grep -v "^STDOUT")
    

    ""

    si vous savez que stdout et/ou stderr sont de forme restreinte, vous pouvez trouver une étiquette qui n'entre pas en conflit avec leur contenu autorisé.

    0
    répondu Warbo 2016-06-02 21:19:54

    avertissement: pas (encore?) WORKING!

    ce qui suit semble une piste possible pour le faire fonctionner sans créer de fichiers temp et aussi sur POSIX sh seulement; il nécessite base64 cependant et en raison de l'encodage/décodage peut ne pas être aussi efficace et utiliser aussi" plus grande " mémoire.

    • même dans le cas simple, il serait déjà échoué, lorsque la dernière ligne stderr n'a pas de nouvelle ligne. Cela peut être fixée au moins dans certains cas remplacer exe par " {exe ; echo >&2;}", c'est-à-dire ajouter une nouvelle ligne.
    • le problème principal est cependant que tout semble osé. Essayez d'utiliser un exe comme:

      exe() { cat/usr/share/hunspell / de_DE.DIC cat /usr/share/hunspell/en_GB.dic > & 2 }

    et vous verrez que, par exemple, des parties de la ligne encodée base64 sont sur le dessus du fichier, des parties à la fin, et la substance non-décodée stderr au milieu.

    bien, même si l'idée ci-dessous ne peut pas être rendue opérationnelle (ce que je suppose), elle peut servir d'anti-exemple pour les gens qui peuvent croire faussement qu'elle pourrait être rendue opérationnelle comme ceci.

    Idée (ou contre-exemple):

    #!/bin/sh
    
    exe()
    {
            echo out1
            echo err1 >&2
            echo out2
            echo out3
            echo err2 >&2
            echo out4
            echo err3 >&2
            echo -n err4 >&2
    }
    
    
    r="$(  { exe  |  base64 -w 0 ; }  2>&1 )"
    
    echo RAW
    printf '%s' "$r"
    echo RAW
    
    o="$( printf '%s' "$r" | tail -n 1 | base64 -d )"
    e="$( printf '%s' "$r" | head -n -1  )"
    unset r    
    
    echo
    echo OUT
    printf '%s' "$o"
    echo OUT
    echo
    echo ERR
    printf '%s' "$e"
    echo ERR
    

    donne (avec le stderr-saut de ligne fixe):

    $ ./ggg 
    RAW
    err1
    err2
    err3
    err4
    
    b3V0MQpvdXQyCm91dDMKb3V0NAo=RAW
    
    OUT
    out1
    out2
    out3
    out4OUT
    
    ERR
    err1
    err2
    err3
    err4ERR
    

    (au moins sur Dash et bash de Debian)

    0
    répondu calestyo 2016-07-27 18:13:42