commande eval dans Bash et ses utilisations typiques

165

Après avoir lu les pages de manuel bash et par rapport à ce post .

J'ai encore du mal à comprendre ce que fait exactement la evalcommande et quelles seraient ses utilisations typiques. Par exemple, si nous faisons:

bash$ set -- one two three  # sets $1 $2 $3
bash$ echo $1
one
bash$ n=1
bash$ echo ${$n}       ## First attempt to echo $1 using brackets fails
bash: ${$n}: bad substitution
bash$ echo $($n)       ## Second attempt to echo $1 using parentheses fails
bash: 1: command not found
bash$ eval echo \${$n} ## Third attempt to echo $1 using 'eval' succeeds
one

Que se passe-t-il exactement ici et comment le signe dollar et la barre oblique inverse sont-ils liés au problème?

kstratis
la source
1
Pour mémoire, la deuxième tentative fonctionne. $($n)s'exécute $ndans un sous-shell. Il essaie d'exécuter la commande 1qui n'existe pas.
Martin Wickman
1
@MartinWickman Mais la condition est de s'exécuter echo $1finalement, non 1. Je ne pense pas que cela puisse être fait en utilisant des sous-shell.
Hari Menon
5
Vous devez être conscient des implications de sécurité de l'utilisationeval .
Suspendu jusqu'à nouvel ordre.
1
@ Raze2dust: Je ne crois pas qu'il suggérait qu'il pourrait être exécuté avec des sous-shell, mais expliquait plutôt pourquoi la 5ème commande répertoriée par l'OP ne fonctionnait pas.
jedwards

Réponses:

196

evalprend une chaîne comme argument et l'évalue comme si vous aviez tapé cette chaîne sur une ligne de commande. (Si vous passez plusieurs arguments, ils sont d'abord joints avec des espaces entre eux.)

${$n}est une erreur de syntaxe dans bash. À l'intérieur des accolades, vous ne pouvez avoir qu'un nom de variable, avec quelques préfixes et suffixes possibles, mais vous ne pouvez pas avoir de syntaxe bash arbitraire et en particulier vous ne pouvez pas utiliser l'expansion de variable. Il y a une manière de dire «la valeur de la variable dont le nom est dans cette variable», cependant:

echo ${!n}
one

$(…)exécute la commande spécifiée entre parenthèses dans un sous-shell (c'est-à-dire dans un processus séparé qui hérite de tous les paramètres tels que les valeurs des variables du shell actuel), et rassemble sa sortie. Donc, echo $($n)s'exécute $ncomme une commande shell et affiche sa sortie. Depuis $névalue à 1, $($n)tente d'exécuter la commande 1, qui n'existe pas.

eval echo \${$n}exécute les paramètres passés à eval. Après l'expansion, les paramètres sont echoet ${1}. Alors eval echo \${$n}lance la commande echo ${1}.

Notez que la plupart du temps, vous devez utiliser des guillemets doubles autour des substitutions de variables et des substitutions de commandes (c'est-à-dire à chaque fois qu'il y a un $):"$foo", "$(foo)" . Mettez toujours des guillemets doubles autour des substitutions de variables et de commandes , sauf si vous savez que vous devez les laisser désactivées. Sans les guillemets doubles, le shell effectue un fractionnement de champ (c'est-à-dire qu'il divise la valeur de la variable ou la sortie de la commande en mots séparés) et traite ensuite chaque mot comme un motif générique. Par exemple:

$ ls
file1 file2 otherfile
$ set -- 'f* *'
$ echo "$1"
f* *
$ echo $1
file1 file2 file1 file2 otherfile
$ n=1
$ eval echo \${$n}
file1 file2 file1 file2 otherfile
$eval echo \"\${$n}\"
f* *
$ echo "${!n}"
f* *

evaln'est pas utilisé très souvent. Dans certains shells, l'utilisation la plus courante est d'obtenir la valeur d'une variable dont le nom n'est pas connu avant l'exécution. Dans bash, ce n'est pas nécessaire grâce à la ${!VAR}syntaxe. evalest toujours utile lorsque vous devez construire une commande plus longue contenant des opérateurs, des mots réservés, etc.

Gilles 'SO- arrête d'être méchant'
la source
En ce qui concerne mon commentaire ci-dessus, combien de "passes" fait l'évaluation?
kstratis
@ Konos5 Quel commentaire? evalreçoit une chaîne (qui peut elle-même être le résultat d'une analyse et d'une évaluation) et l'interprète comme un extrait de code.
Gilles 'SO- arrête d'être diabolique'
Sous la réponse de Raze2dust, j'ai laissé un commentaire. J'ai maintenant tendance à croire que eval est principalement utilisé à des fins de déréférencement. Si je tape eval echo \ $ {$ n} j'en ai un. Cependant si je tape echo \ $ {$ n} j'obtiens \ $ {1}. Je crois que cela se produit en raison de l'analyse en "deux passes" d'eval. Je me demande maintenant ce qui se passerait si je devais tripler le déréférencement en utilisant une déclaration supplémentaire i = n. Dans ce cas, selon Raze2dust, j'ai juste besoin de mettre une évaluation supplémentaire. Je crois cependant qu'il devrait y avoir un meilleur moyen ... (il peut être facilement encombré)
kstratis
@ Konos5 Je n'utiliserais pas eval eval. Je ne me souviens pas avoir jamais ressenti le besoin. Si vous avez vraiment besoin de deux evalpasses, utilisez une variable temporaire, il sera plus facile de débogage: eval tmp="\${$i}"; eval x="\${$tmp}".
Gilles 'SO- arrête d'être diabolique'
1
@ Konos5 "Analysé deux fois" est légèrement trompeur. Certaines personnes pourraient être amenées à le croire en raison de la difficulté de spécifier un argument de chaîne littérale dans Bash qui est protégé de diverses extensions. evalprend juste le code dans une chaîne et l'évalue selon les règles habituelles. Techniquement, ce n'est même pas correct, car il y a quelques cas secondaires dans lesquels Bash modifie l'analyse pour ne même pas effectuer d'extensions des arguments d'eval - mais c'est une information très obscure que je doute que quiconque sache.
ormaaj
39

Pensez simplement à eval comme à "évaluer votre expression une fois de plus avant l'exécution"

eval echo \${$n}devient echo $1après le premier cycle d'évaluation. Trois changements à noter:

  • Le \$devient $(la barre oblique inverse est nécessaire, sinon elle essaie d'évaluer ${$n}, ce qui signifie une variable nommée{$n} , ce qui n'est pas autorisé)
  • $n a été évalué à 1
  • Les evaldisparus

Au deuxième tour, il s'agit essentiellement echo $1 ce qui peut être directement exécuté.

Alors eval <some command>va d'abord évaluer<some command> (par évaluer ici, je veux dire remplacer les variables, remplacer les caractères échappés par les bons, etc.), puis exécuter à nouveau l'expression résultante.

evalest utilisé lorsque vous souhaitez créer dynamiquement des variables ou lire les sorties de programmes spécialement conçus pour être lus comme ceci. Voir http://mywiki.wooledge.org/BashFAQ/048 pour des exemples. Le lien contient également des méthodes typiques d' evalutilisation et les risques qui y sont associés.

Hari Menon
la source
3
En guise de note pour la première puce, la ${VAR}syntaxe est autorisée et préférée en cas d'ambiguïté (fait $VAR == $V, suivie ARou $VAR == $VAsuivie de R). ${VAR}équivaut à $VAR. En fait, son nom de variable $nn'est pas autorisé.
jedwards
2
eval eval echo \\\${\${$i}}fera un triple déréférencement. Je ne sais pas s'il existe un moyen plus simple de procéder. En outre, \${$n}fonctionne très bien (impressions one) sur ma machine ..
Hari Menon
2
@ Konos5 echo \\\${\${$i}}imprime \${${n}}. eval echo \\\${\${$i}}équivaut à echo \${${n}}`` and prints $ {1} . eval eval echo \\\ $ {\ $ {$ i}} `équivaut à eval echo ${1}et imprime one.
Gilles 'SO- arrête d'être diabolique'
2
@ Konos5 Pensez dans le même sens - Le premier ` escapes the second one, and the third `échappe à l' $après. Donc ça devient \${${n}}après un tour d'évaluation
Hari Menon
2
@ Konos5 De gauche à droite est la bonne façon de penser à l'analyse des citations et des barres obliques inverses. \\ Donnant d' abord une barre oblique inverse. Puis \$cédant un dollar. Etc.
Gilles 'SO- arrête d'être diabolique'
25

D'après mon expérience, une utilisation «typique» de eval est pour exécuter des commandes qui génèrent des commandes shell pour définir des variables d'environnement.

Peut-être avez-vous un système qui utilise une collection de variables d'environnement, et vous avez un script ou un programme qui détermine celles qui doivent être définies et leurs valeurs. Chaque fois que vous exécutez un script ou un programme, il s'exécute dans un processus fourchu, donc tout ce qu'il fait directement aux variables d'environnement est perdu lorsqu'il se termine. Mais ce script ou programme peut envoyer les commandes d'exportation à stdout.

Sans eval, vous devrez rediriger stdout vers un fichier temporaire, rechercher le fichier temporaire, puis le supprimer. Avec eval, vous pouvez simplement:

eval "$(script-or-program)"

Notez que les citations sont importantes. Prenons cet exemple (artificiel):

# activate.sh
echo 'I got activated!'

# test.py
print("export foo=bar/baz/womp")
print(". activate.sh")

$ eval $(python test.py)
bash: export: `.': not a valid identifier
bash: export: `activate.sh': not a valid identifier
$ eval "$(python test.py)"
I got activated!
suie
la source
avez-vous des exemples d'outils communs qui font cela? L'outil lui-même a un moyen de produire un ensemble de commandes shell qui peuvent être passées à eval?
Joakim Erdfelt
@Joakim Je ne connais aucun outil open source qui le fasse, mais il a été utilisé dans certains scripts privés d'entreprises où j'ai travaillé. Je viens de recommencer à utiliser cette technique moi-même avec xampp. Les fichiers Apache .conf développent les variables d'environnement écrites ${varname}. Je trouve pratique d'utiliser des fichiers .conf identiques sur plusieurs serveurs différents avec juste quelques éléments paramétrés par des variables d'environnement. J'ai édité / opt / lampp / xampp (qui démarre apache) pour faire ce genre d'évaluation avec un script qui fouille dans le système et génère des exportinstructions bash pour définir des variables pour les fichiers .conf.
sootsnoot
@Joakim L'alternative serait d'avoir un script pour générer chacun des fichiers .conf affectés à partir d'un modèle, basé sur la même fouille. Une chose que j'aime mieux dans ma façon de faire est que démarrer apache sans passer par / opt / lampp / xampp n'utilise pas de scripts de sortie obsolètes, mais au contraire, il ne démarre pas car les variables d'environnement s'étendent à rien et créent des directives invalides.
sootsnoot
@Anthony Sottile Je vois que vous avez modifié la réponse pour ajouter des guillemets autour de $ (script-ou-programme), en disant qu'ils étaient importants lors de l'exécution de plusieurs commandes. Pouvez-vous s'il vous plaît fournir un exemple - ce qui suit fonctionne très bien avec les commandes séparées par des points-virgules dans la sortie standard de foo.sh: echo '#! / Bin / bash'> foo.sh; echo 'echo "echo -na; echo -nb; echo -n c"' >> foo.sh; chmod 755 foo.sh; eval $ (./ foo.sh). Cela produit abc sur stdout. L'exécution de ./foo.sh produit: echo -na; echo -nb; echo -nc
sootsnoot
1
Pour obtenir un exemple d'outil courant utilisant eval, consultez pyenv . pyenv vous permet de basculer facilement entre plusieurs versions de Python. Vous mettez eval "$(pyenv init -)"dans votre .bash_profilefichier de configuration shell (ou similaire). Cela construit un petit script shell puis l'évalue dans le shell actuel.
Jerry101
10

L'instruction eval indique au shell de prendre les arguments d'eval comme commande et de les exécuter via la ligne de commande. C'est utile dans une situation comme ci-dessous:

Dans votre script, si vous définissez une commande dans une variable et que vous souhaitez utiliser ultérieurement cette commande, vous devez utiliser eval:

/home/user1 > a="ls | more"
/home/user1 > $a
bash: command not found: ls | more
/home/user1 > # Above command didn't work as ls tried to list file with name pipe (|) and more. But these files are not there
/home/user1 > eval $a
file.txt
mailids
remote_cmd.sh
sample.txt
tmp
/home/user1 >
Nikhil Gupta
la source
4

Mise à jour: certaines personnes disent qu'il ne faut jamais utiliser eval. Je ne suis pas d'accord. Je pense que le risque survient lorsque des entrées corrompues peuvent être transmises eval. Cependant, il existe de nombreuses situations courantes dans lesquelles ce n'est pas un risque, et il vaut donc la peine de savoir comment utiliser eval dans tous les cas. Cette réponse de stackoverflow explique les risques de eval et les alternatives à eval. En fin de compte, il appartient à l'utilisateur de déterminer si / quand eval est sûr et efficace à utiliser.


Le bash eval instruction vous permet d'exécuter des lignes de code calculées ou acquises, par votre script bash.

L'exemple le plus simple serait peut-être un programme bash qui ouvre un autre script bash sous forme de fichier texte, lit chaque ligne de texte et utilise evalpour les exécuter dans l'ordre. C'est essentiellement le même comportement que le bashsource instruction , qui est celle que l'on utiliserait, à moins qu'il ne soit nécessaire d'effectuer une sorte de transformation (par exemple, filtrage ou substitution) sur le contenu du script importé.

J'en ai rarement eu besoin eval, mais j'ai trouvé utile de lire ou d'écrire des variables dont les noms étaient contenus dans des chaînes affectées à d'autres variables. Par exemple, pour effectuer des actions sur des ensembles de variables, tout en conservant une faible empreinte de code et en évitant la redondance.

evalest conceptuellement simple. Cependant, la syntaxe stricte du langage bash et l'ordre d'analyse de l'interpréteur bash peuvent être nuancés et donner l' evalimpression d'être cryptiques et difficiles à utiliser ou à comprendre. Voici l'essentiel:

  1. L'argument passé à evalest une expression de chaîne calculée au moment de l'exécution. evalexécutera le résultat analysé final de son argument comme une ligne de code réelle dans votre script.

  2. La syntaxe et l'ordre d'analyse sont stricts. Si le résultat n'est pas une ligne exécutable de code bash, dans la portée de votre script, le programme plantera sur l' evalinstruction lorsqu'il essaiera d'exécuter des ordures.

  3. Lors du test, vous pouvez remplacer l' evalinstruction par echoet voir ce qui est affiché. S'il s'agit d'un code légitime dans le contexte actuel, son exécution evalfonctionnera.


Les exemples suivants peuvent aider à clarifier le fonctionnement de eval ...

Exemple 1:

eval l'instruction devant le code 'normal' est un NOP

$ eval a=b
$ eval echo $a
b

Dans l'exemple ci-dessus, les premières evalinstructions n'ont aucun but et peuvent être éliminées. evalest inutile dans la première ligne car il n'y a pas d'aspect dynamique dans le code, c'est-à-dire qu'il est déjà analysé dans les dernières lignes du code bash, donc il serait identique à une instruction normale de code dans le script bash. Le 2ème evalest également inutile, car, bien qu'il y ait une étape d'analyse convertissant $aen son équivalent de chaîne littérale, il n'y a pas d'indirection (par exemple, pas de référencement via la valeur de chaîne d'un nom bash réel ou d'une variable de script tenue par bash), donc il se comporterait de la même manière comme une ligne de code sans le evalpréfixe.



Exemple 2:

Effectuez une affectation var en utilisant des noms var passés sous forme de valeurs de chaîne

$ key="mykey"
$ val="myval"
$ eval $key=$val
$ echo $mykey
myval

Si vous le faisiez echo $key=$val, le résultat serait:

mykey=myval

Cela , étant le résultat final de l'analyse de chaîne, c'est ce qui sera exécuté par eval, d'où le résultat de l'instruction echo à la fin ...



Exemple 3:

Ajout de plus d'indirection à l'exemple 2

$ keyA="keyB"
$ valA="valB"
$ keyB="that"
$ valB="amazing"
$ eval eval \$$keyA=\$$valA
$ echo $that
amazing

Ce qui précède est un peu plus compliqué que l'exemple précédent, s'appuyant davantage sur l'ordre d'analyse et les particularités de bash. La evalligne serait à peu près analysée en interne dans l'ordre suivant (notez que les instructions suivantes sont du pseudocode, pas du vrai code, juste pour essayer de montrer comment l'instruction serait décomposée en étapes en interne pour arriver au résultat final) .

 eval eval \$$keyA=\$$valA  # substitution of $keyA and $valA by interpreter
 eval eval \$keyB=\$valB    # convert '$' + name-strings to real vars by eval
 eval $keyB=$valB           # substitution of $keyB and $valB by interpreter
 eval that=amazing          # execute string literal 'that=amazing' by eval

Si l'ordre d'analyse supposé n'explique pas suffisamment ce que fait eval, le troisième exemple peut décrire l'analyse plus en détail pour aider à clarifier ce qui se passe.



Exemple 4:

Découvrez si les variables, dont les noms sont contenus dans des chaînes, contiennent elles-mêmes des valeurs de chaîne.

a="User-provided"
b="Another user-provided optional value"
c=""

myvarname_a="a"
myvarname_b="b"
myvarname_c="c"

for varname in "myvarname_a" "myvarname_b" "myvarname_c"; do
    eval varval=\$$varname
    if [ -z "$varval" ]; then
        read -p "$varname? " $varname
    fi
done

Dans la première itération:

varname="myvarname_a"

Bash analyse l'argument de eval, et evalvoit littéralement ceci au moment de l'exécution:

eval varval=\$$myvarname_a

Le pseudo- code suivant tente d'illustrer comment bash interprète la ligne de code réel ci-dessus , pour arriver à la valeur finale exécutée par eval. (les lignes suivantes descriptives, pas de code bash exact):

1. eval varval="\$" + "$varname"      # This substitution resolved in eval statement
2. .................. "$myvarname_a"  # $myvarname_a previously resolved by for-loop
3. .................. "a"             # ... to this value
4. eval "varval=$a"                   # This requires one more parsing step
5. eval varval="User-provided"        # Final result of parsing (eval executes this)

Une fois que toute l'analyse est terminée, le résultat est ce qui est exécuté, et son effet est évident, démontrant qu'il n'y a rien de particulièrement mystérieux en evallui-même, et la complexité réside dans l' analyse de son argument.

varval="User-provided"

Le code restant dans l'exemple ci-dessus teste simplement pour voir si la valeur affectée à $ varval est nulle et, si tel est le cas, invite l'utilisateur à fournir une valeur.

clearlight
la source
3

Au départ, je n'ai jamais appris à utiliser eval, car la plupart des gens recommanderont de rester à l'écart comme la peste. Cependant j'ai récemment découvert un cas d'utilisation qui m'a fait facepalm pour ne pas l'avoir reconnu plus tôt.

Si vous avez des tâches cron que vous souhaitez exécuter de manière interactive pour tester, vous pouvez afficher le contenu du fichier avec cat, puis copier et coller la tâche cron pour l'exécuter. Malheureusement, cela implique de toucher la souris, ce qui est un péché dans mon livre.

Disons que vous avez un travail cron à /etc/cron.d/repeatme avec le contenu:

*/10 * * * * root program arg1 arg2

Vous ne pouvez pas l'exécuter en tant que script avec tous les indésirables devant lui, mais nous pouvons utiliser cut pour se débarrasser de tous les indésirables, l'envelopper dans un sous-shell et exécuter la chaîne avec eval

eval $( cut -d ' ' -f 6- /etc/cron.d/repeatme)

La commande cut n'imprime que le 6ème champ du fichier, délimité par des espaces. Eval exécute ensuite cette commande.

J'ai utilisé un travail cron ici comme exemple, mais le concept est de formater le texte de stdout, puis d'évaluer ce texte.

Dans ce cas, l'utilisation de eval n'est pas anodine, car nous savons exactement ce que nous allons évaluer à l'avance.

Luke Pafford
la source
2

J'ai récemment dû utiliser evalpour forcer plusieurs expansions d'accolades à être évaluées dans l'ordre dont j'avais besoin. Bash fait plusieurs extensions d'accolades de gauche à droite, donc

xargs -I_ cat _/{11..15}/{8..5}.jpg

s'étend à

xargs -I_ cat _/11/8.jpg _/11/7.jpg _/11/6.jpg _/11/5.jpg _/12/8.jpg _/12/7.jpg _/12/6.jpg _/12/5.jpg _/13/8.jpg _/13/7.jpg _/13/6.jpg _/13/5.jpg _/14/8.jpg _/14/7.jpg _/14/6.jpg _/14/5.jpg _/15/8.jpg _/15/7.jpg _/15/6.jpg _/15/5.jpg

mais j'avais besoin de la deuxième extension d'accolade faite en premier, cédant

xargs -I_ cat _/11/8.jpg _/12/8.jpg _/13/8.jpg _/14/8.jpg _/15/8.jpg _/11/7.jpg _/12/7.jpg _/13/7.jpg _/14/7.jpg _/15/7.jpg _/11/6.jpg _/12/6.jpg _/13/6.jpg _/14/6.jpg _/15/6.jpg _/11/5.jpg _/12/5.jpg _/13/5.jpg _/14/5.jpg _/15/5.jpg

Le mieux que j'ai pu trouver pour faire ça a été

xargs -I_ cat $(eval echo _/'{11..15}'/{8..5}.jpg)

Cela fonctionne car les guillemets simples protègent le premier ensemble d'accolades de l'expansion lors de l'analyse de la evalligne de commande, les laissant étendus par le sous-shell appelé par eval.

Il peut y avoir un schéma astucieux impliquant des extensions d'accolades imbriquées qui permet que cela se produise en une seule étape, mais s'il y en a, je suis trop vieux et stupide pour le voir.

flabdablet
la source
1

Vous avez posé des questions sur les utilisations typiques.

Une plainte courante concernant les scripts shell est que vous ne pouvez (prétendument) pas passer par référence pour récupérer des valeurs des fonctions.

Mais en fait, via "eval", vous pouvez passer par référence. L'appelé peut renvoyer une liste d'attributions de variables à évaluer par l'appelant. Il est passé par référence car l'appelant peut être autorisé à spécifier le (s) nom (s) de la ou des variables de résultat - voir l'exemple ci-dessous. Les résultats d'erreur peuvent être renvoyés par des noms standard tels que errno et errstr.

Voici un exemple de passage par référence dans bash:

#!/bin/bash
isint()
{
    re='^[-]?[0-9]+$'
    [[ $1 =~ $re ]]
}

#args 1: name of result variable, 2: first addend, 3: second addend 
iadd()
{
    if isint ${2} && isint ${3} ; then
        echo "$1=$((${2}+${3}));errno=0"
        return 0
    else
        echo "errstr=\"Error: non-integer argument to iadd $*\" ; errno=329"
        return 1
    fi
}

var=1
echo "[1] var=$var"

eval $(iadd var A B)
if [[ $errno -ne 0 ]]; then
    echo "errstr=$errstr"
    echo "errno=$errno"
fi
echo "[2] var=$var (unchanged after error)"

eval $(iadd var $var 1)
if [[ $errno -ne 0 ]]; then
    echo "errstr=$errstr"
    echo "errno=$errno"
fi  
echo "[3] var=$var (successfully changed)"

La sortie ressemble à ceci:

[1] var=1
errstr=Error: non-integer argument to iadd var A B
errno=329
[2] var=1 (unchanged after error)
[3] var=2 (successfully changed)

Il y a une largeur de bande presque illimitée dans cette sortie de texte! Et il y a plus de possibilités si les multiples lignes de sortie sont utilisées: par exemple, la première ligne pourrait être utilisée pour des affectations de variables, la seconde pour un «flux de pensée» continu, mais cela dépasse le cadre de cet article.

Craig Hicks
la source
dire qu'il y a «plus de possibilités» est pour le moins insignifiant, trivial et redondant.
dotbit
0

J'aime la réponse "évaluer votre expression une fois de plus avant l'exécution" et je voudrais clarifier avec un autre exemple.

var="\"par1 par2\""
echo $var # prints nicely "par1 par2"

function cntpars() {
  echo "  > Count: $#"
  echo "  > Pars : $*"
  echo "  > par1 : $1"
  echo "  > par2 : $2"

  if [[ $# = 1 && $1 = "par1 par2" ]]; then
    echo "  > PASS"
  else
    echo "  > FAIL"
    return 1
  fi
}

# Option 1: Will Pass
echo "eval \"cntpars \$var\""
eval "cntpars $var"

# Option 2: Will Fail, with curious results
echo "cntpars \$var"
cntpars $var

Les résultats curieux de l'option 2 sont que nous aurions passé 2 paramètres comme suit:

  • Premier paramètre: "value
  • Deuxième paramètre: content"

En quoi cela est-il contre-intuitif? L'ajout evalcorrigera cela.

Adapté de https://stackoverflow.com/a/40646371/744133

Yo-yo
la source
0

Dans la question:

who | grep $(tty | sed s:/dev/::)

génère des erreurs affirmant que les fichiers a et tty n'existent pas. J'ai compris que cela signifiait que tty n'était pas interprété avant l'exécution de grep, mais à la place que bash passait tty comme paramètre à grep, qui l'interprétait comme un nom de fichier.

Il existe également une situation de redirection imbriquée, qui devrait être gérée par des parenthèses correspondantes qui devraient spécifier un processus enfant, mais bash est primitivement un séparateur de mots, créant des paramètres à envoyer à un programme, donc les parenthèses ne sont pas mises en correspondance en premier, mais interprétées comme vu.

Je suis devenu spécifique avec grep et j'ai spécifié le fichier comme paramètre au lieu d'utiliser un tuyau. J'ai également simplifié la commande de base, en passant la sortie d'une commande sous forme de fichier, de sorte que le tuyau d'E / S ne soit pas imbriqué:

grep $(tty | sed s:/dev/::) <(who)

fonctionne bien.

who | grep $(echo pts/3)

n'est pas vraiment souhaité, mais élimine le tuyau imbriqué et fonctionne également bien.

En conclusion, bash ne semble pas aimer le pipping imbriqué. Il est important de comprendre que bash n'est pas un programme nouvelle vague écrit de manière récursive. Au lieu de cela, bash est un ancien programme 1,2,3, qui a été ajouté avec des fonctionnalités. Pour garantir la compatibilité ascendante, la manière initiale d'interprétation n'a jamais été modifiée. Si bash était réécrit pour correspondre en premier aux parenthèses, combien de bogues seraient introduits dans combien de programmes bash? De nombreux programmeurs aiment être cryptiques.

Paul
la source