Bash Linux: assegnazione di più variabili


121

Esiste in Linux bash qualcosa di simile al seguente codice in PHP:

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

cioè si assegna in una frase un valore corrispondente a 3 diverse variabili.

Diciamo che ho la funzione bash myBashFuntionche scrive su stdout la stringa "qwert asdfg zxcvb". È possibile fare qualcosa come:

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

La parte a sinistra del segno di uguale non è ovviamente una sintassi valida. Sto solo cercando di spiegare cosa sto chiedendo.

Quello che funziona, però, è il seguente:

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

Ma un array indicizzato non è così descrittivo come nomi di variabili semplici.
Tuttavia, potrei semplicemente fare:

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

Ma queste sono altre 3 affermazioni che preferirei evitare.

Sto solo cercando una sintassi di scorciatoia. È possibile?

Risposte:


222

La prima cosa che mi viene in mente:

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

l'output è, non sorprende

1|2|3

4
C'è comunque per farlo funzionare se la prima variabile contiene uno spazio?
Rucent88

7
@Michael Using read -d "\n" v1 v2 <<<$(cmd)funziona perfettamente. Grazie!
Rucent88

1
@LeeNetherton, buon punto, anche se non sono sicuro che sia necessario lo stato di ritorno del comando echo :-) Penso che al momento della stesura della risposta la bash che supportava questa sintassi fosse meno comune (come installato di default), sebbene io Non sono sicuro al 100%.
Michael Krelin - hacker

4
@ MichaelKrelin-hacker certo, lo stato di ritorno di echoè inutile, ma stavo usando questa tecnica per restituire più valori da uno script che mi interessava dello stato di ritorno. Ho pensato di condividere le mie scoperte.
Lee Netherton,

1
Per motivi di sicurezza si dovrebbe usare: read -r:do not allow backslashes to escape any characters
Tom Hale

18

Volevo assegnare i valori a un array. Quindi, estendendo l'approccio di Michael Krelin , ho fatto:

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

che produce:

2|4|6 

come previsto.


2
Per mettere i valori in un array esiste già una semplice soluzione che ho menzionato nella domanda:a=( $(echo 2 4 6) ) ; echo ${a[0]} ${a[1]} ${a[2]}
GetFree

Sì, l'avevo trascurato. Direi, tuttavia, che il mio suggerimento è più adatto all'assegnazione di array più grandi.
soundray

@soundray La tua soluzione utilizza l'espansione e un herestring, essendo bash quello che è, dubito che funzionerà bene in quello scenario (ma non ho controllato).
Camilo Martin

Per motivi di sicurezza si dovrebbe usare: read -r:do not allow backslashes to escape any characters
Tom Hale

5

Penso che questo potrebbe aiutare ...

Per suddividere le date inserite dall'utente (mm / gg / aaaa) nei miei script, memorizzo il giorno, il mese e l'anno in un array, quindi inserisco i valori in variabili separate come segue:

DATE_ARRAY=(`echo $2 | sed -e 's/\// /g'`)
MONTH=(`echo ${DATE_ARRAY[0]}`)
DAY=(`echo ${DATE_ARRAY[1]}`)
YEAR=(`echo ${DATE_ARRAY[2]}`)

Perché non evitare 4 subshell più un processo sed extra e fare tutto in una riga:IFS=/ read -r m d y < <(echo 12/29/2009)
Amit Naidu,

5

A volte devi fare qualcosa di strano. Supponiamo che tu voglia leggere da un comando (l'esempio della data di SDGuero ad esempio) ma vuoi evitare più fork.

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

Potresti anche inserire il comando read, ma poi dovresti usare le variabili all'interno di una subshell:

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

risultati in ...

13 08 2013
n/a n/a n/a

Il readcomando non viene eseguito in una subshell a causa delle parentesi graffe, è perché hai il comando di lettura sul lato destro della pipe. Devi eseguire il readcomando nella shell corrente, cosa che puoi fareread day month year <<< `date "+%d %m %Y"`
pneumatica

No, read succede ma l'ambito delle variabili in cui legge non rientra quando termina la sottoshell della pipeline.
Otheus

1
Il mio commento riguardava il motivo per cui la lettura avviene in una subshell, e ora mi rendo conto di aver letto male quello che hai scritto. Pensavo volessi dire che la subshell è stata creata perché hai usato le parentesi graffe attorno all'istruzione composta. Ma! Il motivo per cui hai fornito questo esempio era per evitare il fork, ma non lo farà anche il subshell fork?
pneumatica

Il primo esempio richiede esattamente un fork: in modo che bash possa lanciare il comando date. Nel secondo esempio, si imposta una pipeline tra la data e la sottostruttura. Penso che bash in questi giorni sia abbastanza intelligente da non fare il fork nella subshell, ma non ne sono sicuro. In ogni caso, sembra che sarebbe :)
Otheus

0

Il capitolo 5 del Bash Cookbook di O'Reilly, discute (a lungo) le ragioni del requisito in un'assegnazione variabile che non ci siano spazi intorno al segno '='

MYVAR="something"

La spiegazione ha qualcosa a che fare con la distinzione tra il nome di un comando e una variabile (dove "=" può essere un argomento valido).

Tutto questo sembra un po 'come giustificare dopo l'evento, ma in ogni caso non si parla di un metodo di assegnazione a un elenco di variabili.


Si, lo so. Ho appena aggiunto spazi extra qua e là per motivi di leggibilità
GetFree

In effetti questa è una scarsa motivazione: e se " ;" fosse un argomento valido? Quando scrivo ls ; cdchiama ancora lse cdnonostante gli spazi. Se voglio elencare le directory chiamate ;e cdposso semplicemente digitare ls ';' cd.
PieterNuyts
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.