web-dev-qa-db-fra.com

Capturez stdout et stderr dans différentes variables

Est-il possible de stocker ou capturer stdout et stderr dans différentes variables, sans utiliser de fichier temporaire? En ce moment, je fais cela pour obtenir stdout dans out et stderr dans err lors de l'exécution de some_command, mais j'aimerais éviter le fichier temporaire.

error_file=$(mktemp)
out=$(some_command 2>$error_file)
err=$(< error_file)
rm $error_file
59
ntc2

Ok, c'est devenu 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 réelle. La sortie de stdout est enregistrée dans le tableau $t_std Ligne par ligne en omettant les sauts de ligne (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 imite à peu près le comportement de var=$(cmd) à l'exception de 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).

36
TheConstructor

Jonathan a la réponse . Pour référence, c'est l'astuce ksh93. (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;} N'est qu'une substitution de commandes qui ne crée pas de sous-shell. Les commandes sont exécutées dans l'environnement Shell actuel. L'espace au début est important ({ Est un mot réservé).

Stderr du groupe de commandes interne est redirigé vers stdout (afin qu'il s'applique à la substitution interne). Ensuite, la sortie standard de out est affectée à y, et le stderr redirigé est capturé par x, sans la perte habituelle de y à la substitution d'une commande. sous-coquille.

Ce n'est pas possible dans d'autres shells, car toutes les constructions qui capturent la sortie nécessitent de placer le producteur dans une sous-coque, qui dans ce cas, inclurait l'affectation.

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

15
ormaaj

Cette commande définit à la fois les valeurs stdout (stdval) et stderr (errval) dans le shell en cours d'exécution:

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

à condition que cette fonction soit définie:

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

Remplacez execcommand par la commande capturée, que ce soit "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)

Enveloppez tout dans un appel d'exécution et faites-le écho:

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

Vous obtiendrez les appels de déclaration que chaque setval crée:

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

Pour exécuter ce code (et obtenir le jeu de variables), utilisez eval:

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

et enfin faire écho aux vars définis:

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

Il est également possible d'inclure la valeur de retour (sortie).
Un exemple de script bash complet ressemble à ceci:

#!/bin/bash --

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

# 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|"
14
user2350426

C'est pour attraper stdout et stderr dans différentes variables. Si vous voulez seulement attraper stderr, en laissant stdout tel quel, il existe une solution meilleure et plus courte .

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

Réutilisation facile bash Solution

Cette version utilise des sous-coquilles et fonctionne sans tempfiles. (Pour une version tempfile qui fonctionne sans sous-shell, voir mon autre réponse .)

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

Exemple d'utilisation:

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

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"

cela imprime

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

Il peut donc être utilisé sans y réfléchir plus profondément. Mettez simplement catch VAR1 VAR2 Devant tout 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 enveloppe simplement les idées des autres réponses ici dans une fonction, de sorte qu'il peut facilement être réutilisé.

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

Considérez un appel de catch out err dummy 1 2a 3b:

  • sautons le eval "$({ et le __2="$( pour l'instant. J'y reviendrai plus tard.

  • __1="$("$("${@:3}")"; } 2>&1; exécute dummy 1 2 3 et stocke son stdout dans __1 pour une utilisation ultérieure. Donc __1 Devient 2a. Il redirige également stderr de dummy vers stdout, de sorte que la capture externe peut rassembler stdout

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

  • printf '%q=%q\n' "$1" "$__1" >&2; Renvoie ensuite out=2a Vers stderr. stderr est utilisé ici, car le stdout actuel 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.

Passons maintenant à la __2="$( ... )" extérieure:

  • Cela capture stdout de ce qui précède, qui est le stderr de l'appel dummy, dans la variable __2. (Nous pourrions réutiliser __1 Ici, mais j'ai utilisé __2 Pour le rendre moins déroutant.). Donc __2 Devient 3b

  • ret="$?"; Récupère le code retour (retourné) 1 (De dummy) à nouveau

  • printf '%s=%q\n' "$2" "$__2" >&2; Renvoie ensuite err=3a Vers stderr. stderr est à nouveau utilisé, car il était déjà utilisé pour sortir 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 catch`.

Veuillez noter que, comme optimisation, nous aurions pu écrire ces 2 printf comme un seul comme printf '%s=%q\n( exit %q ) "$ __ 2" "$ ret" `également.

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

Nous avons écrit à stderr:

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

out vient de $1, 2a vient de stdout de dummy, err vient de $2, 3b Provient de stderr de dummy, et 1 Provient du code de retour de dummy.

Veuillez noter que %q Au format printf prend soin de la citation, de telle sorte que le Shell voit les arguments (uniques) appropriés quand il s'agit de eval. 2a Et 3b Sont si simples qu'ils sont copiés littéralement.

Passons maintenant à la eval "$({ ... } 2>&1 )"; extérieure:

Cela exécute tout ce qui précède qui génère les 2 variables et le exit, l'attrape (à cet effet le 2>&1) Et l'analyse dans le shell actuel en utilisant eval.

De cette façon, les 2 variables sont définies ainsi que le code retour.

Q: Il utilise eval ce qui est mauvais. Est-ce donc sûr?

  • Tant que printf %q N'a pas de bogues, il devrait être sûr. Mais vous devez toujours être très prudent, pensez à Shellshock.

Q: Des bugs?

  • Aucun bogue évident n'est connu, à l'exception des suivants:

    • La capture de grandes sorties nécessite une mémoire et un processeur importants, car tout va dans les variables et doit être analysé par le shell. Alors utilisez-le à bon escient.
    • Comme d'habitude $(echo $'\n\n\n\n') avale tous les sauts de ligne , pas seulement le dernier. Il s'agit d'une exigence POSIX. Si vous avez besoin d'obtenir les LF indemnes, ajoutez simplement un caractère de fin à la sortie et supprimez-le ensuite comme dans la recette suivante (regardez le x de fin qui permet de lire un lien logiciel pointant vers un fichier qui se termine sur un $'\n'):

      target="$(readlink -e "$file")x"
      target="${target%x}"
      
    • Les variables shell ne peuvent pas porter l'octet NUL ($'\0'). Ils sont simplement ignorés s'ils se produisent dans stdout ou stderr.

  • La commande donnée s'exécute dans un sous-sous-shell. Il n'a donc pas accès à $PPID, Ni ne peut modifier les variables Shell. Vous pouvez catch une fonction Shell, même les fonctions internes, mais celles-ci ne pourront pas modifier les variables Shell (car tout ce qui s'exécute dans $( .. ) ne peut pas le faire). Donc, si vous devez exécuter une fonction dans le shell actuel et attraper son stderr/stdout, vous devez le faire de la manière habituelle avec tempfiles. (Il existe des moyens de le faire de telle sorte que l'interruption du shell ne laisse normalement pas de débris, mais c'est complexe et mérite sa propre réponse.)

Q: version Bash?

  • Je pense que vous avez besoin de Bash 4 et supérieur (en raison de printf %q)

Q: Cela semble toujours aussi gênant.

  • Droite. ne autre réponse ici montre comment cela peut être fait dans ksh beaucoup plus proprement. Cependant, je n'ai pas l'habitude de ksh, je laisse donc à d'autres le soin de créer une recette similaire facile à réutiliser pour ksh.

Q: Pourquoi ne pas utiliser ksh alors?

  • Parce que c'est une solution bash

Q: Le script peut être amélioré

  • Bien sûr, vous pouvez extraire certains octets et créer une solution plus petite ou plus incompréhensible. Allez-y;)

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

  • En fait, cela 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 faute de frappe dans la définition de la fonction. C'est une vieille astuce de débogage. Mais attention, vous pouvez facilement créer des effets secondaires soignés dans les arguments de :.

Edit: Ajout de quelques ; De plus pour faciliter la création d'une doublure simple à partir de catch(). Et ajouté une section comment cela fonctionne.

12
Tino

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

#!/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 d'arrière-plan de cette façon et collecter de manière asynchrone leurs stdouts et stderrs à un moment opportun, etc.

Si vous en avez besoin pour un seul processus, vous pouvez tout aussi bien utiliser des nombres fd codés en dur comme 3 et 4, au lieu de {fdout}/{fderr} syntaxe (qui trouve un fd gratuit pour vous).

9
Irfy

N'aimait pas l'eval, voici donc une solution qui utilise des astuces de redirection pour capturer la sortie du programme dans une variable, puis analyse cette variable pour extraire les différents composants. L'indicateur -w définit la taille du bloc et influe sur l'ordre des messages std-out/err au format intermédiaire. 1 donne une résolution potentiellement élevée au détriment des 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 "$1" "$c"                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
          done                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
        )                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
      )                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
    }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
    ( (                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
        "$@" | prefix o >&3                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        echo  ${PIPESTATUS[0]} | prefix r >&3                                                                                                                                                                                                                                                                                                                                                                                                                                                           
      ) 2>&1 | prefix e >&1                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
    ) 3>&1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
  )                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       

extractOne() { # extract                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
  echo "$2" | grep "^$1" | cut --delimiter=' ' --fields=2 | base64 --decode -                                                                                                                                                                                                                                                                                                                                                                                                                           
}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
3
mncl

En résumé, je crois que la réponse est "non". La capture $( ... ) capture uniquement la sortie standard vers la variable; il n'y a aucun moyen d'obtenir l'erreur standard capturée dans une variable distincte. Donc, ce que vous avez est aussi soigné que possible.

2
Jonathan Leffler

Qu'en est-il de ... = D

GET_STDERR=""
GET_STDOUT=""
get_stderr_stdout() {
    GET_STDERR=""
    GET_STDOUT=""
    unset t_std t_err
    eval "$( (eval $1) 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
Eduardo Lucio

Pour le bénéfice du lecteur, voici une solution utilisant tempfiles.

La question n'était pas d'utiliser tempfiles. Cependant, cela peut être dû à la pollution indésirable de /tmp/ Avec tempfile au cas où le Shell meurt. Dans le cas de kill -9 Certains trap 'rm "$tmpfile1" "$tmpfile2"' 0 Ne se déclenchent pas.

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

Encore une fois, il est appelé catch() (comme ma 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 "$3=\"\`cat <&$2\`\"";
# 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$2 -d '' "$3";
return $1;
}
: catch_1 tempfile variable comand args..
catch_1()
{
{
rm -f "$1";
"${@:3}" 66<&-;
catch_read $? 66 "$2";
} 2>&1 >"$1" 66<"$1";
}

: 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 pour stdout et stderr. Cependant, il les supprime presque immédiatement, de sorte qu'ils ne sont disponibles que très peu de temps.

  • catch_1() capture stdout (FD 1) dans une variable et déplace stderr vers stdout, de sorte que la prochaine ("gauche") catch_1 peut attraper ça.

  • Le traitement dans catch se fait de droite à gauche, donc le catch_1 De gauche est exécuté en dernier et attrape stderr.

Le pire qui puisse arriver est que certains fichiers temporaires apparaissent sur /tmp/, Mais ils sont toujours vides dans ce cas. (Ils sont retirés avant d'être remplis.). Habituellement, cela ne devrait pas poser de problème, car sous Linux, tmpfs prend en charge environ 128 Ko de fichiers par Go de mémoire principale.

  • La commande donnée peut également accéder à toutes les variables Shell locales et les modifier. Vous pouvez donc appeler une fonction Shell qui a des effets secondaires!

  • Cela ne bifurque que deux fois pour l'appel tempfile.

Bugs:

  • Manque une bonne gestion des erreurs en cas d'échec de tempfile.

  • Cela fait la suppression habituelle \n Du Shell. Voir le commentaire dans catch_read().

  • Vous ne pouvez pas utiliser le descripteur de fichier 66 Pour diriger les données vers votre commande. Si vous en avez besoin, utilisez un autre descripteur pour la redirection, comme 42 (Notez que les très anciens shells ne proposent que des FD jusqu'à 9).

  • Cela ne peut pas gérer les octets NUL ($'\0') Dans stdout et stderr. (NUL est simplement ignoré. Pour la variante read tout derrière un NUL est ignoré.)

Pour info:

  • Unix nous permet d'accéder aux fichiers supprimés, tant que vous gardez une référence à eux (comme un descripteur de fichier ouvert). De cette façon, nous pouvons les ouvrir puis les supprimer.
2
Tino

Une solution de contournement, qui est hacky mais peut-être plus intuitive que certaines des suggestions de cette page, consiste à baliser les flux de sortie, à les fusionner et à les diviser ensuite en fonction des balises. Par exemple, nous pouvons é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 créer une balise qui n'entre pas en conflit avec leur contenu autorisé.

0
Warbo

Voici une variation 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 fichiers.

Commande de test:

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

qui en soi:

%> ./xx.sh
stdout
stderr

Maintenant, imprimez stdout, capturez stderr dans une variable et connectez stdout dans un fichier

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

Ou connectez stdout et capturez stderr dans une variable:

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

Vous avez eu l'idée.

0
Bruce Edge

ATTENTION: PAS (encore?) FONCTIONNANT!

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

  • Même dans le cas simple, il échouerait déjà, lorsque la dernière ligne stderr n'a pas de nouvelle ligne. Cela peut être corrigé au moins dans certains cas en remplaçant exe par "{exe; echo> & 2;}", c'est-à-dire en ajoutant une nouvelle ligne.
  • Le principal problème est cependant que tout semble racé. 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 en base64 se trouvent en haut du fichier, des parties à la fin et les éléments stderr non décodés au milieu.

Eh bien, même si l'idée ci-dessous ne peut pas être mise en œuvre (ce que je suppose), elle peut servir d'anti-exemple pour les personnes qui pourraient croire à tort qu'elle pourrait être mise en œuvre de cette façon.

Idée (ou anti-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 correctif stderr-newline):

$ ./ggg 
RAW
err1
err2
err3
err4

b3V0MQpvdXQyCm91dDMKb3V0NAo=RAW

OUT
out1
out2
out3
out4OUT

ERR
err1
err2
err3
err4ERR

(Au moins sur le tableau de bord et bash de Debian)

0
calestyo

Si la commande 1) aucun effet secondaire avec état et 2) est bon marché sur le plan des calculs, la solution la plus simple consiste à l'exécuter deux fois. J'ai principalement utilisé cela pour le code qui s'exécute pendant la séquence de démarrage lorsque vous ne savez pas encore si le disque va fonctionner. Dans mon cas, c'était un minuscule some_command donc il n'y a pas eu de perte de performances pour avoir exécuté deux fois, et la commande n'a eu aucun effet secondaire.

Le principal avantage est qu'il est propre et facile à lire. Les solutions ici sont assez intelligentes, mais je détesterais être celle qui doit maintenir un script contenant les solutions les plus compliquées. Je recommanderais l'approche simple à exécuter deux fois 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, ce n'est correct que parce que getopt n'a aucun effet secondaire. Je sais que les performances sont sûres car mon code parent l'appelle moins de 100 fois pendant tout le programme, et l'utilisateur ne remarquera jamais 100 appels getopt contre 200 appels getopt.

0
Hamy