Las otras respuestas se romperá si la salida del comando contiene espacios (lo cual es bastante frecuente) o Glob caracteres como *, ?, [...].
Para obtener el resultado de un comando en una matriz, con una línea por elemento, existen esencialmente 3 formas:
Con el uso de Bash≥4 mapfile, es el más eficiente:
mapfile -t my_array < <( my_command )
De lo contrario, un bucle que lee la salida (más lento, pero seguro):
my_array=()
while IFS= read -r line; do
my_array+=( "$line" )
done < <( my_command )
Como sugirió Charles Duffy en los comentarios (¡gracias!), Lo siguiente podría funcionar mejor que el método de bucle en el número 2:
IFS=$'\n' read -r -d '' -a my_array < <( my_command && printf '\0' )
Asegúrese de utilizar exactamente este formulario, es decir, asegúrese de tener lo siguiente:
IFS=$'\n' en la misma línea que la readdeclaración: esto solo establecerá la variable de entorno solo IFS para la readdeclaración. Por lo tanto, no afectará en absoluto al resto del guión. El propósito de esta variable es indicar readque se rompa la secuencia en el carácter EOL \n.
-r: esto es importante. Dice read que no interprete las barras invertidas como secuencias de escape.
-d '': tenga en cuenta el espacio entre la -dopción y su argumento ''. Si no deja un espacio aquí, ''nunca se verá, ya que desaparecerá en el paso de eliminación de citas cuando Bash analice la declaración. Esto le dice readque deje de leer en el byte nulo. Algunas personas lo escriben como -d $'\0', pero no es realmente necesario. -d ''es mejor.
-a my_arrayle dice readque llene la matriz my_arraymientras lee la secuencia.
- Debe usar la
printf '\0'instrucción after my_command , para que readregrese 0; en realidad, no es gran cosa si no lo hace (solo obtendrá un código de retorno 1, lo cual está bien si no lo usa set -e, que de todos modos no debería), pero téngalo en cuenta. Es más limpio y semánticamente correcto. Tenga en cuenta que esto es diferente de printf '', que no genera nada. printf '\0'imprime un byte nulo, necesario readpara dejar felizmente de leer allí (¿recuerdas la -d ''opción?).
Si puede, es decir, si está seguro de que su código se ejecutará en Bash≥4, utilice el primer método. Y puedes ver que también es más corto.
Si desea usar read, el ciclo (método 2) podría tener una ventaja sobre el método 3 si desea realizar algún procesamiento a medida que se leen las líneas: tiene acceso directo a él (a través de la $linevariable en el ejemplo que di), y también tiene acceso a las líneas ya leídas (a través de la matriz ${my_array[@]}en el ejemplo que di).
Tenga en cuenta que mapfileproporciona una manera de que se evalúe una devolución de llamada en cada línea leída y, de hecho, incluso puede decirle que solo llame a esta devolución de llamada cada N líneas leídas; eche un vistazo a help mapfilelas opciones -Cy -callí. (Mi opinión sobre esto es que es un poco torpe, pero se puede usar a veces si solo tienes cosas simples que hacer; ¡realmente no entiendo por qué esto se implementó en primer lugar!).
Ahora les voy a decir por qué el siguiente método:
my_array=( $( my_command) )
se rompe cuando hay espacios:
$ # 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!
Entonces, algunas personas recomendarán usarlo IFS=$'\n'para solucionarlo:
$ 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!
Pero ahora usemos otro 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?
Eso es porque tengo un archivo llamado ten el directorio actual ... y este nombre de archivo coincide con el glob [three four] ... en este punto, algunas personas recomendarían usarlo set -fpara deshabilitar el globbing: pero míralo: tienes que cambiar IFSy usar set -fpara poder arreglar un técnica rota (y ni siquiera la estás arreglando realmente)! al hacer eso, realmente estamos luchando contra el caparazón, no trabajando con el caparazón .
$ mapfile -t my_array < <( echo "* one two"; echo "[three four]")
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="[three four]")'
aquí estamos trabajando con el caparazón!