Le altre risposte si rompa se output del comando contiene spazi (che è piuttosto frequente) o glob caratteri come *, ?, [...].
Per ottenere l'output di un comando in un array, con una riga per elemento, ci sono essenzialmente 3 modi:
Con Bash≥4 mapfile, è il più efficiente:
mapfile -t my_array < <( my_command )
Altrimenti, un ciclo che legge l'output (più lento, ma sicuro):
my_array=()
while IFS= read -r line; do
my_array+=( "$line" )
done < <( my_command )
Come suggerito da Charles Duffy nei commenti (grazie!), Quanto segue potrebbe funzionare meglio del metodo loop al numero 2:
IFS=$'\n' read -r -d '' -a my_array < <( my_command && printf '\0' )
Assicurati di utilizzare esattamente questo modulo, ovvero assicurati di avere quanto segue:
IFS=$'\n' sulla stessa riga readdell'istruzione: questo imposterà solo la variabile d'ambiente solo IFS per l' readistruzione. Quindi non influenzerà affatto il resto del tuo script. Lo scopo di questa variabile è indicare readdi interrompere il flusso in corrispondenza del carattere EOL \n.
-r: questo è importante. Indica read di non interpretare i backslash come sequenze di escape.
-d '': notare lo spazio tra l' -dopzione e il suo argomento ''. Se non lasci uno spazio qui, ''non verrà mai visualizzato, poiché scomparirà nella fase di rimozione delle virgolette quando Bash analizzerà l'istruzione. Questo dice readdi interrompere la lettura al byte nullo. Alcune persone lo scrivono come -d $'\0', ma non è realmente necessario. -d ''è meglio.
-a my_arraydice readdi popolare l'array my_arraydurante la lettura del flusso.
- È necessario utilizzare l'
printf '\0'istruzione dopo my_command , in modo che readritorni 0; in realtà non è un grosso problema se non lo fai (riceverai solo un codice di ritorno 1, che va bene se non lo usi set -e, cosa che non dovresti comunque), ma tienilo a mente. È più pulito e semanticamente corretto. Nota che questo è diverso da printf '', che non restituisce nulla. printf '\0'stampa un byte nullo, necessario readper smettere felicemente di leggere lì (ricordi l' -d ''opzione?).
Se puoi, cioè se sei sicuro che il tuo codice verrà eseguito su Bash≥4, usa il primo metodo. E puoi vedere che è anche più corto.
Se si desidera utilizzare read, il ciclo (metodo 2) potrebbe avere un vantaggio rispetto al metodo 3 se si desidera eseguire un'elaborazione mentre le righe vengono lette: si ha accesso diretto ad esso (tramite la $linevariabile nell'esempio che ho fornito) e hai anche accesso alle righe già lette (tramite l'array ${my_array[@]}nell'esempio che ho dato).
Nota che mapfilefornisce un modo per avere un callback eval'd su ogni riga letta, e infatti puoi anche dirgli di chiamare solo questo callback ogni N righe lette; dare un'occhiata alle help mapfileopzioni -Ce al contenuto -c. (La mia opinione su questo è che è un po 'goffo, ma a volte può essere usato se hai solo cose semplici da fare - non capisco davvero perché sia stato implementato in primo luogo!).
Ora ti dirò perché il seguente metodo:
my_array=( $( my_command) )
è rotto quando ci sono spazi:
$ # I'm using this command to test:
$ echo "one two"; echo "three four"
one two
three four
$ # Now I'm going to use the broken method:
$ my_array=( $( echo "one two"; echo "three four" ) )
$ declare -p my_array
declare -a my_array='([0]="one" [1]="two" [2]="three" [3]="four")'
$ # As you can see, the fields are not the lines
$
$ # Now look at the correct method:
$ mapfile -t my_array < <(echo "one two"; echo "three four")
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # Good!
Quindi alcune persone consiglieranno di utilizzare IFS=$'\n'per risolverlo:
$ IFS=$'\n'
$ my_array=( $(echo "one two"; echo "three four") )
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # It works!
Ma ora usiamo un altro comando, con globs :
$ echo "* one two"; echo "[three four]"
* one two
[three four]
$ IFS=$'\n'
$ my_array=( $(echo "* one two"; echo "[three four]") )
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="t")'
$ # What?
Questo perché ho un file chiamato tnella directory corrente ... e questo nome di file è abbinato al glob [three four] ... a questo punto alcune persone consiglierebbero di utilizzare set -fper disabilitare il globbing: ma guarda: devi cambiare IFSe usare set -fper poter aggiustare un tecnica rotta (e non la stai nemmeno aggiustando davvero)! quando lo facciamo stiamo davvero combattendo contro la shell, non lavorando con la shell .
$ mapfile -t my_array < <( echo "* one two"; echo "[three four]")
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="[three four]")'
qui stiamo lavorando con la shell!