Comment enregistrer la sortie d'une commande qui modifie l'environnement en une variable?
J'utilise bash shell.
Supposons que j'ai:
function f () { a=3; b=4 ; echo "`date`: $a $b"; }
Et maintenant, je peux utiliser des commandes pour exécuter f
:
$ a=0; b=0; f; echo $a; echo $b; echo $c
Sat Jun 28 21:27:08 CEST 2014: 3 4
3
4
mais je voudrais enregistrer la sortie de la f
variable c
, j'ai donc essayé:
a=0; b=0; c=""; c=$(f); echo $a; echo $b; echo $c
mais malheureusement, j'ai:
0
0
Sat Jun 28 21:28:03 CEST 2014: 3 4
donc je n'ai pas de changement d'environnement ici.
Comment enregistrer la sortie de la commande (pas seulement la fonction) dans une variable et enregistrer les changements environnementaux?
Je sais que cela $(...)
ouvre un nouveau sous-shell et c'est le problème, mais est-il possible de contourner ce problème?
bash
environment-variables
output
faramir
la source
la source
$a
et$b
sont des variables locales dans votref
fonction. Vous pourriezexport
les faire, mais cela semble sommaire.…; f; echo $a; …
entraîne3
un écho, toutf
comme la modification d'une variable shell (et pas seulement sa propre variable locale).Réponses:
Si vous utilisez Bash 4 ou une version ultérieure, vous pouvez utiliser des coprocessus :
affichera
coproc
crée un nouveau processus exécutant une commande donnée (ici,cat
). Il enregistre le PIDCOPROC_PID
et les descripteurs de fichiers d'entrée / sortie standard dans un tableauCOPROC
(commepipe(2)
, ou voir ici ou ici ).Ici, nous exécutons la fonction avec une sortie standard pointée vers notre coprocessus en cours d'exécution
cat
, puis àread
partir de celle-ci. Puisquecat
juste crache son entrée, nous obtenons la sortie de la fonction dans notre variable.exec {COPROC[1]}>&-
ferme simplement le descripteur de fichier afin decat
ne pas attendre indéfiniment.Notez que cela
read
ne prend qu'une seule ligne à la fois. Vous pouvez utilisermapfile
pour obtenir un tableau de lignes, ou simplement utiliser le descripteur de fichier comme vous le souhaitez d'une manière différente.exec {COPROC[1]}>&-
travaux dans les versions actuelles de Bash, mais les versions antérieures de la série 4 nécessitent d'enregistrer le descripteur de fichier dans une variable simple première:fd=${COPROC[1]}; exec {fd}>&-
. Si votre variable n'est pas définie, elle fermera la sortie standard.Si vous utilisez une version 3 de Bash, vous pouvez obtenir le même effet
mkfifo
, mais ce n'est pas beaucoup mieux que d'utiliser un fichier réel à ce stade.la source
exec {COPROC[1]}>&-
commande (au moins parfois) termine immédiatement le coprocessus , donc sa sortie n'est plus disponible pour être lue.coproc { cat; sleep 1; }
semble fonctionner mieux. (Vous devrez peut-être augmenter la valeur1
si vous lisez plusieurs lignes.)Si vous comptez déjà sur le corps de l'
f
exécution dans le même shell que l'appelant, et donc être en mesure de modifier des variables commea
etb
, pourquoi ne pas définir la fonctionc
aussi bien? En d'autres termes:Une raison possible peut être que la variable de sortie doit avoir des noms différents (c1, c2, etc.) lorsqu'elle est appelée à différents endroits, mais vous pouvez résoudre ce problème en définissant la fonction c_TEMP et en demandant aux appelants de le faire,
c1=$c_TEMP
etc.la source
C'est un kluge, mais essayez
(et, éventuellement,
rm c.file
).la source
mktemp
, mais je ne voudrais pas créer de fichiers supplémentaires.Attribuez simplement toutes les variables et écrivez la sortie en même temps.
Maintenant, si vous le faites:
Votre sortie est:
Notez qu'il s'agit entièrement de code portable POSIX. J'ai initialement défini
c=
la''
chaîne nulle car l'expansion des paramètres limite l'affectation + l'évaluation simultanée des variables à des valeurs uniquement numériques (comme$((var=num))
) ou à des valeurs nulles ou inexistantes - ou, en d'autres termes, vous ne pouvez pas définir et évaluer simultanément une variable en une chaîne arbitraire si cette variable est déjà affectée à une valeur. Je m'assure donc qu'il est vide avant d'essayer. Si je ne vidais pasc
avant d'essayer de l'assigner, l'expansion ne retournerait que l'ancienne valeur.Juste pour démontrer:
newval
n'est pas affecté à$c
inline caroldval
est développé dans${word}
, tandis que l' affectation$((
arithmétique inline se produit toujours. Mais si n'a pas et est vide ou non défini ...=
))
$c
oldval
...
newval
est alors à la fois affecté et développé$c
.Toutes les autres façons de procéder impliquent une forme d'évaluation secondaire. Par exemple, supposons que je souhaite affecter la sortie de
f()
à une variable nomméename
à un moment etvar
à un autre. Tel qu'il est actuellement écrit, cela ne fonctionnera pas sans définir la var dans la portée de l'appelant. Une manière différente pourrait ressembler à ceci, cependant:J'ai fourni un meilleur exemple formaté ci-dessous, mais, appelé comme ci-dessus, la sortie est:
Ou avec des
$ENV
arguments différents ou:La chose la plus délicate à faire pour évaluer deux fois est probablement de s'assurer que les variables ne cassent pas les guillemets et exécutent du code aléatoire. Plus une variable est évaluée, plus elle devient difficile. L'extension des paramètres aide beaucoup ici, et l'utilisation
export
par opposition àeval
est beaucoup plus sûre.Dans l'exemple ci-dessus,
f()
attribue d'abord$fout
la''
chaîne nulle, puis définit les paramètres de position pour tester les noms de variables valides. Si les deux tests ne réussissent pas, un message est émisstderr
et la valeur par défaut defout
est affectée à$fout_name
. Indépendamment des tests, cependant,$fout_name
est toujours affecté à l'unfout
ou au nom que vous spécifiez$fout
et, éventuellement, votre nom spécifié reçoit toujours la valeur de sortie de la fonction. Pour illustrer cela, j'ai écrit cette petitefor
boucle:Il joue avec certains noms de variables et extensions de paramètres. Si vous avez une question, il suffit de la poser. Cela n'exécute que les mêmes lignes dans la fonction déjà représentée ici. Il convient de mentionner au moins que les variables
$a
et$b
se comportent différemment selon qu'elles sont définies lors de l'invocation ou déjà définies. Pourtant, lefor
ne fait presque rien d'autre que formater l'ensemble de données et fourni parf()
. Regarde:la source