Les affectations de variables affectent le shell courant de fonctionnement

En écrivant du code, j'ai découvert que cette ligne:

$ TZ="America/Los_Angeles" date; echo "$TZ" Thu Dec 24 14:39:15 PST 2015 

Indique correctement l'heure réelle à "Los Angeles" et que la valeur de la variable TZ n'est pas conservée. Tout comme on devrait s'y attendre.

Cependant, avec cette ligne, que j'ai utilisé pour get des formats développés à ce jour, et qui exécute essentiellement la même chose, conserve la valeur de TZ:

 TZ="America/Los_Angeles" eval date; echo "$TZ" Thu Dec 24 14:41:34 PST 2015 America/Los_Angeles 

Après plusieurs tests supplémentaires, j'ai découvert que cela n'arrive que dans certains obus. Cela arrive dans le tiret, ksh mais pas dans bash ou zsh.

Q's

La ou les questions sont les suivantes:

  • Pourquoi la valeur de TZ est-elle retenue dans la coquille actuelle?
  • Comment cela pourrait-il être évité / contrôlé (si possible)?

Supplémentaire.

J'ai couru des tests dans plusieurs coquilles avec ces deux lignes:

 myTZ="America/Los_Angeles" unset TZ; { TZ="$myTZ" date; } >/dev/null; echo -n " direct $TZ" unset TZ; { TZ="$myTZ" eval date; } >/dev/null; echo " evaled $TZ" 

Et ceci résulte:

 /bin/ash : direct evaled America/Los_Angeles /bin/dash : direct evaled America/Los_Angeles /bin/sh : direct evaled America/Los_Angeles /bin/bash : direct evaled /bin/ksh93 : direct evaled America/Los_Angeles /bin/lksh : direct evaled America/Los_Angeles /bin/mksh : direct evaled America/Los_Angeles /bin/zsh : direct evaled /bin/zsh4 : direct evaled 

La valeur TZ affecte le shell en cours d'exécution dans tous les shells sauf bash et zsh.

Comme vous l'avez trouvé, c'est un comportement spécifique. Mais cela a aussi du sens.

La valeur est conservée dans l'environnement du shell pour la même raison que la valeur des autres variables d'environnement est conservée par d'autres commands lorsque vous préfixez des définitions à leurs lignes de command – vous définissez les variables dans leur environnement.

Les builtins spéciaux sont généralement la variété la plus insortingnsèque de n'importe quel shell – eval est essentiellement un nom accessible pour l'parsingur du shell, set pistes et configure les options shell et les parameters du shell, return / coupe / continue le stream de contrôle de la boucle de triggersment / ferme les files. Ce sont tous des utilitaires fondamentaux – et sont généralement mis en œuvre avec des emballages à peine là-dessus sur la viande et les pommes de terre de votre coquille.

L'exécution de la plupart des commands implique un environnement en couches – un environnement sousshell (qui ne doit pas forcément être un process distinct) – ce qui n'est pas le cas lorsque vous appelez les builtins spéciaux. Ainsi, lorsque vous définissez l'environnement pour l'une de ces commands, vous définissez l'environnement pour votre shell. Parce qu'ils représentent essentiellement votre coquille.

Mais ce ne sont pas les seules commands qui conservent l'environnement de cette façon – les fonctions font de même. Et les erreurs se comportent différemment pour les built-ins spéciaux – essayez cat <doesntexist et essayez try exec <doesntexist ou même juste : <doesntexist et pendant que la command cat va se plaindre, l' exec ou : va tuer un shell POSIX. Il en est de même pour les erreurs d'extension sur leur command line. Ils sont la boucle principale , essentiellement.

Ces commands n'ont pas besoin de retenir l'environnement – certaines coquilles enveloppent leurs internes plus étroitement que d'autres, exposent less de fonctionnalités de base et ajoutent plus de tampon entre le programmeur et l'interface. Ces mêmes coquilles pourraient aussi avoir tendance à être un peu plus lentes que d'autres. Définitivement, ils nécessitent beaucoup d'ajustements non standard pour les rendre conforms aux spécifications. Et de toute façon, ce n'est pas comme si c'était une mauvaise chose:

 fn(){ bad_command || return=$some_value return; } 

Ce truc est facile . Sinon, comment préserveriez-vous le return de bad_command si vous n'avez pas à créer un tas d'environnement supplémentaire et que vous faites des tâches conditionnellement?

 arg=$1 shift; x=$y unset y 

Ce genre de choses fonctionne aussi. En place, les échanges sont plus simples.

 IFS=+ set -- "$IFS" xyz x="$*" IFS=$1 shift echo "${x#"$IFS"}" "$*" 

 +x+y+zxyz 

…ou…

 expand(){ PS4="$*" set -x "" "$PS4" { $1; } 2>&1 PS4=$2 set +x } 2>/dev/null x='echo kill my computer; $y' y='haha! just kidding!' expand "${x##*[\`\(]*}" 

… est un autre que j'aime utiliser …

 echo kill my computer; haha! just kidding! 

Il s'avère qu'il y a une raison très spécifique pour ce comportement.
La description de ce qui se passe est un peu plus longue.

Seulement des devoirs.

Une command line faite (seulement) d'affectations définira les variables pour ce shell.

 $ unset abcd $ a=bc=d $ echo "<$a::$c>" <b::d> 

La valeur des vars assignés sera conservée.

Commande externe.

Les affectations avant une command externe définissent des variables pour ce shell uniquement:

 $ unset abcd $ a=bc=d bash -c 'echo "one:|$c|"'; echo "two:<$c>" one:|d| two:<> 

Et je veux dire "externe" comme n'importe quelle command qui doit être recherchée dans PATH.

Cela s'applique également aux built-ins normaux (comme cd, par exemple):

 $ unset abcd; a=bc=d cd . ; echo "<$a::$c>" <::> 

Jusqu'à ici tout est comme c'est habituellement prévu.

Special Built-Ins.

Mais pour les built-ins spéciaux, POSIX exige que les valeurs soient définies pour ce shell .

  1. Les affectations de variables spécifiées avec des utilitaires embeddeds spéciaux restnt en vigueur une fois l'intégration terminée.
 $ sh -c 'unset abcd; a=bc=d export f=g ; echo "<$a::$c::$f>"' <b::d::g> 

J'utilise un appel à sh supposant que sh est un shell compatible POSIX.

Ce n'est pas quelque chose qui est habituellement utilisé.

Cela signifie que les assignations placées devant l'une quelconque de cette list d'encarts spéciaux conservent les valeurs atsortingbuées dans le shell en cours de fonctionnement:

 break : continue . eval exec exit export readonly return set shift times trap unset 

Cela se produira si un shell fonctionne selon la spécification POSIX.

Conclusion:

Il est possible de définir des variables pour une seule command, n'importe quelle command, en s'assurant que la command n'est pas un embedded spécial. La command command est un builtin régulier. Il indique seulement au shell d'utiliser une command, pas une fonction. Cette ligne fonctionne dans tous les shells (excepté ksh93):

 $ unset abcd; a=bc=d command eval 'f=g'; echo "<$a::$c::$f>" <::::g> 

Dans ce cas, les vars a et b sont définis pour l'environnement de la command, puis rejetés après cela.

Au lieu de cela, cela conservera les valeurs assignées (sauf bash et zsh):

 $ unset abcd; a=bc=d eval 'f=g'; echo "<$a::$c::$f>" <b::d::g> 

Notez que l'affectation après eval est citée pour le protéger contre les extensions indésirables.

Donc: Pour placer des variables dans l'environnement de command, utilisez la command eval :