Lectura de una cadena delimitada en una matriz en Bash

207

Tengo una variable que contiene una cadena delimitada por espacios:

line="1 1.50 string"

Quiero dividir esa cadena con espacio como delimitador y almacenar el resultado en una matriz, de modo que lo siguiente:

echo ${arr[0]}
echo ${arr[1]}
echo ${arr[2]}

salidas

1
1.50
string

En algún lugar encontré una solución que no funciona:

arr=$(echo ${line})

Si ejecuto las declaraciones de eco anteriores después de esto, obtengo:

1 1.50 string
[empty line]
[empty line]

También intenté

IFS=" "
arr=$(echo ${line})

con el mismo resultado ¿Puede alguien ayudar, por favor?

Nikola Novak
fuente
Vea esta respuesta en Unix & Linux Stack Exchange: Uso de sed con herestring (<<<) y lea -a . set -f; arr=($string); set +fParece ser más rápido que read -r -a <<< $string.
codeforester

Respuestas:

332

Para convertir una cadena en una matriz, utilice

arr=($line)

o

read -a arr <<< $line

Es crucial no usar comillas ya que esto funciona.

kev
fuente
77
y para hacer un chequeo de cordura de su hermosa nueva gama:for i in ${arr[@]}; do echo $i; done
Banjer
55
o simplementeecho ${arr[@]}
Banjer
13
Ambas formas pueden fallar si $linetiene caracteres globulares. mkdir x && cd x && touch A B C && line="*" arr=($line); echo ${#arr[@]}da 3
Tino
3
declare -a "arr=($line)"ignorará los IFSdelimitadores dentro de las cadenas citadas
Dave
44
@Tino No. Cuando line='*', read -a arr <<<$linesiempre funciona, pero solo arr=($line)falla.
Johnny Wong
39

Prueba esto:

arr=(`echo ${line}`);
Cachondo
fuente
55
Agradable: esta solución también funciona en Z Shell, donde fallan algunos de los otros enfoques anteriores.
Keith Hughitt
Funciona, ¿podría explicar por qué funciona?
smartwjw
2
Observación: esto tampoco funciona cuando la línea tiene '*', comoline='*'
Johnny Wong
34

En: arr=( $line ). La "división" viene asociada con "glob".
Los comodines ( *, ?y []) se expandirán a nombres de archivo coincidentes.

La solución correcta es solo un poco más compleja:

IFS=' ' read -a arr <<< "$line"

No hay problema de globbing; se establece el carácter dividido $IFS, variables citadas.

Isaac
fuente
55
Esta debería ser la respuesta aceptada. La declaración arr=($line)en la respuesta aceptada adolece de problemas globales. Por ejemplo, trate de: line="twinkling *"; arr=($line); declare -p arr.
codeforester
Las comillas son opcionales para la herejía, <<<pero puede ser una buena idea utilizar comillas dobles para mantener la coherencia y la legibilidad.
codeforester
7

Si necesita expansión de parámetros, intente:

eval "arr=($line)"

Por ejemplo, tome el siguiente código.

line='a b "c d" "*" *'
eval "arr=($line)"
for s in "${arr[@]}"; do 
    echo "$s"
done

Si el directorio actual contiene los archivos a.txt, b.txty c.txt, a continuación, ejecutar el código produciría la siguiente salida.

a
b
c d
*
a.txt
b.txt
c.txt
David Anderson
fuente
-9
line="1 1.50 string"

arr=$( $line | tr " " "\n")

for x in $arr
do
echo "> [$x]"
done
Hitesh
fuente
El bucle es incorrecto, divide bien la matriz y el tubo tres superfluo, pero en su lugar debería "${arr[@]}"$arr
repetirse