leer -a matriz -d '\ n' <foo, código de salida 1

8

Si trato de ejecutar

read -a fooArr -d '\n' < bar

el código de salida es 1, a pesar de que logra lo que yo quiero; pon cada línea de baren un elemento de la matriz fooArr(usando bash 4.2.37).

¿Alguien puede explicar por qué sucede esto?


He encontrado otras formas de resolver esto, como las siguientes, así que no es lo que estoy pidiendo.

for ((i=1;; i++)); do
    read "fooArr$i" || break;
done < bar

o

mapfile -t fooArr < bar
RasmusWL
fuente

Respuestas:

14

Lo que debe explicarse es que el comando parecía funcionar, no su código de salida

'\n'tiene dos caracteres: una barra diagonal inversa \y una letra n. Lo que pensabas que necesitabas era $'\n', que es un salto de línea (pero eso tampoco sería correcto, ver más abajo).

La -dopción hace esto:

  -d delim  continue until the first character of DELIM is read, rather
            than newline

Entonces, sin esa opción, readleería hasta una nueva línea, dividiría la línea en palabras usando los caracteres $IFScomo separadores y colocaría las palabras en la matriz. Si lo especificó -d $'\n', estableciendo el delimitador de línea en una nueva línea, haría exactamente lo mismo . La configuración -d '\n'significa que se leerá hasta la primera barra diagonal inversa (pero, una vez más, vea a continuación), que es el primer carácter en delim. Como no hay una barra invertida en su archivo, readterminará al final del archivo y:

Exit Status:
The return code is zero, unless end-of-file is encountered, read times out,
or an invalid file descriptor is supplied as the argument to -u.

Por eso el código de salida es 1.

Por el hecho de que usted cree que el comando funcionó, podemos concluir que no hay espacios en el archivo, por lo que read, después de leer todo el archivo con la vana esperanza de encontrar una barra invertida, lo dividirá por espacios en blanco (el valor predeterminado de $IFS), incluidas las nuevas líneas. Por lo tanto, cada línea (o cada palabra, si una línea contiene más de una palabra) se almacena en la matriz.

El misterioso caso de la barra invertida robada

Ahora, ¿cómo supe que el archivo no contenía barras invertidas? Porque no proporcionó la -rbandera a read:

  -r                do not allow backslashes to escape any characters

Entonces, si tuviera barras diagonales inversas en el archivo, se habrían eliminado, a menos que tuviera dos de ellas seguidas. Y, por supuesto, existe la evidencia de que readtenía un código de salida de 1, lo que demuestra que no encontró una barra invertida, por lo que tampoco había dos seguidos.

Comida para llevar

Bash no sería bash si no hubiera trampas escondiéndose detrás de casi todos los comandos, y readno es una excepción. Aquí hay una pareja:

  1. A menos que especifique -r, readinterpretará las secuencias de escape de barra invertida. A menos que sea realmente lo que desea (que ocasionalmente es, pero solo ocasionalmente), debe recordar especificar -rpara evitar que los caracteres desaparezcan en el raro caso de que haya barras invertidas en la entrada.

  2. El hecho de que readdevuelva un código de salida de 1 no significa que haya fallado. Bien pudo haber tenido éxito, excepto por encontrar el terminador de línea. Así que tenga cuidado con un ciclo como este: while read -r LINE; do something with LINE; done porque fallará do somethingcon la última línea en el raro caso de que la última línea no tenga una nueva línea al final.

  3. read -r LINE conserva las barras invertidas, pero no conserva los espacios en blanco iniciales o finales.

rici
fuente
¡Gracias! Pensé que había probado el enfoque $ '\ n', pero supongo que no: / es bueno saber sobre el -r sin embargo
RasmusWL
2
"El raro caso de que la última línea no tuviera una nueva línea" no me parece una razón convincente para aconsejar "nunca usar while read". De lo contrario, respuesta fantástica.
Glenn Jackman
@glennjackman: Puedes usar while readsiempre que no tengas nada dentro del bucle. De lo contrario, es un error esperando morderte, y créeme, he sido mordido.
rici
2
La última línea siempre tiene una nueva línea. Así es como se define una línea: termina con una nueva línea. Si un archivo no vacío no termina con una nueva línea, no es un archivo de texto y no puede usar herramientas de procesamiento de texto como la utilidad de shell read.
Gilles 'SO- deja de ser malvado'
2
@glennjackman: Yo, itero a través de archivos en columna con awk, principalmente. Para iterar líneas enteras donde el archivo no es demasiado grande, mapfilees bastante bueno. Para un hack rápido y sucio, usaré el bucle while prohibido, pero he dejado de poner eso en los scripts de producción. YMMV y yo suavizamos la advertencia en la respuesta.
rici
4

Ese es el comportamiento esperado:

El código de retorno es cero, a menos que se encuentre el final del archivo, [...]

start cmd:> echo a b c | { read -a testarray; echo $?; }
0

start cmd:> echo -n a b c | { read -a testarray; echo $?; }
1
Hauke ​​Laging
fuente