Crear una matriz a partir de un archivo de texto en Bash

Una secuencia de comandos toma una URL, la analiza para los campos obligatorios y redirige su salida para que se guarde en un archivo, file.txt . El resultado se guarda en una nueva línea cada vez que se encuentra un campo.

archivo.txt

A Cat A Dog A Mouse etc... 

Quiero tomar file.txt y crear una matriz a partir de él en una nueva secuencia de comandos, donde cada línea llega a ser su propia variable de cadena en la matriz. Hasta ahora lo he intentado:

 #!/bin/bash filename=file.txt declare -a myArray myArray=(`cat "$filename"`) for (( i = 0 ; i < 9 ; i++)) do echo "Element [$i]: ${myArray[$i]}" done 

Cuando ejecuto este script, el espacio en blanco hace que las palabras se dividan y en lugar de obtener

Salida deseada

 Element [0]: A Cat Element [1]: A Dog etc... 

Termino obteniendo esto:

Salida real

 Element [0]: A Element [1]: Cat Element [2]: A Element [3]: Dog etc... 

¿Cómo puedo ajustar el ciclo siguiente para que la cadena completa en cada línea corresponda uno a uno con cada variable en la matriz?

Use el comando mapfile :

 mapfile -t myArray < file.txt 

El error está usando for - la forma idiomática de recorrer las líneas de un archivo es:

 while IFS= read -r line; do echo ">>$line<<"; done < file.txt 

Vea BashFAQ / 005 para más detalles.

Usted puede hacer esto también:

 oldIFS="$IFS" IFS=$'\n' arr=($( 

Nota:

La expansión del nombre de archivo todavía ocurre. Por ejemplo, si hay una línea con un literal * se expandirá a todos los archivos en la carpeta actual. Así que úsela solo si su archivo está libre de este tipo de escenario.

mapfile y readarray (que son sinónimos) están disponibles en Bash versión 4 y superior. Si tiene una versión anterior de Bash, puede usar un ciclo para leer el archivo en una matriz:

 arr=() while IFS= read -r line; do arr+=("$line") done < file 

En caso de que el archivo tenga una última línea incompleta (nueva línea faltante), puede usar esta alternativa:

 arr=() while IFS= read -r line || [[ "$line" ]] do arr+=("$line") done < file 

Relacionado:

  • Necesita una alternativa a readarray / mapfile para script en un sistema anterior

Simplemente puede leer cada línea del archivo y asignarla a una matriz.

 #!/bin/bash i=0 while read line do arr[$i]="$line" i=$((i+1)) done < file.txt 

Use mapfile o lea -a

Siempre revise su código usando shellcheck . A menudo le dará la respuesta correcta. En este caso, SC2207 cubre la lectura de un archivo que tiene espacios separados o valores separados por nueva línea en una matriz.

No hagas esto

 array=( $(mycommand) ) 

Archivos con valores separados por líneas nuevas

 mapfile -t array < <(mycommand) 

Archivos con valores separados por espacios

 IFS=" " read -r -a array <<< "$(mycommand)" 

La página de shellcheck le dará la razón por la cual esto se considera la mejor práctica.

Esta respuesta dice usar

 mapfile -t myArray < file.txt 

Hice un mapfile para mapfile si quieres usar mapfile en bash <4.x por cualquier razón. Utiliza el comando mapfile existente si está en bash> = 4.x

Actualmente, solo funcionan las opciones -d y -t . Pero eso debería ser suficiente para ese comando de arriba. Solo he probado en macOS. En macOS Sierra 10.12.6, el bash del sistema es 3.2.57(1)-release . Así que la cuña puede ser útil. También puedes actualizar tu bash con homebrew, construir bash tú mismo, etc.

Utiliza esta técnica para establecer variables en una stack de llamadas.