Linux bash: affectation de variables multiples

121

Existe-t-il dans linux bash quelque chose de similaire au code suivant en PHP:

list($var1, $var2, $var3) = function_that_returns_a_three_element_array() ;

c'est-à-dire que vous attribuez en une phrase une valeur correspondante à 3 variables différentes.

Disons que j'ai la fonction bash myBashFuntionqui écrit sur stdout la chaîne "qwert asdfg zxcvb". Est-il possible de faire quelque chose comme:

(var1 var2 var3) = ( `myBashFuntion param1 param2` )

La partie à gauche du signe égal n'est bien sûr pas une syntaxe valide. J'essaie juste d'expliquer ce que je demande.

Ce qui fonctionne, cependant, est le suivant:

array = ( `myBashFuntion param1 param2` )
echo ${array[0]} ${array[1]} ${array[2]}

Mais un tableau indexé n'est pas aussi descriptif que les noms de variables simples.
Cependant, je pourrais simplement faire:

var1 = ${array[0]} ; var2 = ${array[1]} ; var3 = ${array[2]}

Mais ce sont 3 autres déclarations que je préférerais éviter.

Je cherche juste une syntaxe de raccourci. C'est possible?

GetFree
la source

Réponses:

222

Première chose qui me vient à l'esprit:

read -r a b c <<<$(echo 1 2 3) ; echo "$a|$b|$c"

la sortie est, sans surprise

1|2|3
Michael Krelin - hacker
la source
4
Est-il possible de faire fonctionner cela si la première variable contient un espace?
Rucent88
7
@Michael Using read -d "\n" v1 v2 <<<$(cmd)fonctionne parfaitement. Je vous remercie!
Rucent88
1
@LeeNetherton, bon point, même si je ne suis pas sûr si l'on a besoin du statut de retour de la commande echo :-) Je pense qu'au moment de la rédaction de la réponse, la bash supportant cette syntaxe était moins courante (comme installée par défaut), bien que je Je ne suis pas sûr à 100%.
Michael Krelin - hacker
4
@ MichaelKrelin-hacker bien sûr, le statut de retour de echoest inutile, mais j'utilisais cette technique pour renvoyer plusieurs valeurs à partir d'un script dont je me souciais du statut de retour. J'ai pensé partager mes découvertes.
Lee Netherton
1
Pour plus de sécurité , vous devez utiliser: read -r:do not allow backslashes to escape any characters
Tom Hale
18

Je voulais attribuer les valeurs à un tableau. Donc, prolongeant l'approche de Michael Krelin , j'ai fait:

read a[{1..3}] <<< $(echo 2 4 6); echo "${a[1]}|${a[2]}|${a[3]}"

ce qui donne:

2|4|6 

comme prévu.

Soundray
la source
2
Pour mettre les valeurs dans un tableau, il existe déjà une solution simple que j'ai mentionnée dans la question:a=( $(echo 2 4 6) ) ; echo ${a[0]} ${a[1]} ${a[2]}
GetFree
Oui, j'avais oublié cela. Je dirais, cependant, que ma suggestion est mieux adaptée à l'attribution de plus grands tableaux.
soundray
@soundray Votre solution utilise l'expansion et un héritage, bash étant ce que c'est, je doute qu'elle fonctionnera bien dans ce scénario (mais je n'ai pas vérifié).
Camilo Martin
Pour plus de sécurité , vous devez utiliser: read -r:do not allow backslashes to escape any characters
Tom Hale
5

Je pense que cela pourrait aider ...

Afin de décomposer les dates saisies par l'utilisateur (mm / jj / aaaa) dans mes scripts, je stocke le jour, le mois et l'année dans un tableau, puis je mets les valeurs dans des variables séparées comme suit:

DATE_ARRAY=(`echo $2 | sed -e 's/\// /g'`)
MONTH=(`echo ${DATE_ARRAY[0]}`)
DAY=(`echo ${DATE_ARRAY[1]}`)
YEAR=(`echo ${DATE_ARRAY[2]}`)
SDGuero
la source
Pourquoi ne pas éviter 4 sous-shell plus un processus sed supplémentaire, et faire tout cela en une seule ligne:IFS=/ read -r m d y < <(echo 12/29/2009)
Amit Naidu
5

Parfois, vous devez faire quelque chose de génial. Supposons que vous souhaitiez lire à partir d'une commande (l'exemple de date de SDGuero par exemple) mais que vous souhaitiez éviter les fourchettes multiples.

read month day year << DATE_COMMAND
 $(date "+%m %d %Y")
DATE_COMMAND
echo $month $day $year

Vous pouvez également accéder à la commande read, mais vous devrez ensuite utiliser les variables dans un sous-shell:

day=n/a; month=n/a; year=n/a
date "+%d %m %Y" | { read day month year ; echo $day $month $year; }
echo $day $month $year

résulte en...

13 08 2013
n/a n/a n/a
Otheus
la source
La readcommande ne se produit pas dans un sous-shell à cause des accolades, c'est parce que vous avez la commande read sur le côté droit du tube. Vous devez exécuter la readcommande dans le shell actuel, ce que vous pouvez faire commeread day month year <<< `date "+%d %m %Y"`
pneumatics
Non - read cela se produit, mais la portée des variables dans lesquelles il lit tombe hors de portée lorsque le sous-shell du pipeline se termine.
Otheus
1
Mon commentaire portait sur la raison pour laquelle la lecture se produit dans un sous-shell, et je me rends compte maintenant que j'ai mal lu ce que vous avez écrit. Je pensais que vous vouliez dire que le sous-shell a été créé parce que vous avez utilisé les accolades autour de l'instruction composée. Mais! La raison pour laquelle vous avez donné cet exemple était d'éviter de bifurquer, mais le sous-coque ne le fera-t-il pas aussi?
pneumatique
Le premier exemple nécessite exactement un fork: pour que bash puisse lancer la commande date. Dans le deuxième exemple, vous configurez un pipeline entre date et votre sous-shell. Je pense que bash ces jours-ci est assez intelligent pour ne pas réellement se faufiler dans le sous-shell, mais je n'en suis pas sûr. En tout cas, on dirait que ce serait le cas :)
Otheus
0

Le chapitre 5 du livre de recettes Bash d'O'Reilly discute (assez longuement) des raisons de l'exigence dans une affectation de variable qu'il n'y ait pas d'espace autour du signe '='

MYVAR="something"

L'explication a quelque chose à voir avec la distinction entre le nom d'une commande et une variable (où «=» peut être un argument valide).

Tout cela ressemble un peu à une justification après l'événement, mais en tout cas il n'y a aucune mention d'une méthode d'affectation à une liste de variables.

Pavium
la source
Oui je sais. Je viens d'ajouter des espaces supplémentaires ici et là pour des raisons de lisibilité
GetFree
En effet, c'est une mauvaise motivation: et si « ;» est un argument valable? Quand j'écris ls ; cdça appelle encore lset cdmalgré les espaces. Si je veux lister les répertoires appelés ;et que cdje peux simplement taper ls ';' cd.
PieterNuyts