Cómo leer la línea completa en el bucle 'for' con espacios


130

Estoy tratando de ejecutar un forbucle para el archivo y quiero mostrar la línea completa. Pero, en cambio, solo muestra la última palabra. Quiero la linea completa.

for j in `cat ./file_wget_med`

do
echo $j

done

resultado después de la ejecución:

Found.

Aquí están mis datos:

$ cat file_wget_med
2013-09-11 14:27:03 ERROR 404: Not Found.

Respuestas:


179

forel bucle se divide cuando ve cualquier espacio en blanco como espacio, tabulación o nueva línea. Por lo tanto, debe usar IFS (separador de campo interno) :

IFS=$'\n'       # make newlines the only separator
for j in $(cat ./file_wget_med)    
do
    echo "$j"
done
# Note: IFS needs to be reset to default!

55
Y tenga cuidado con las comillas simples en IFS porque IFS = $ "\ n" también dividirá las cadenas "nn". Encontré el sistema IFS más confiable que el uso de sintaxis o funciones más complicadas.
erm3nda

23
aparentemente se recomienda unset IFSdespués, para que otros comandos no se vean influenciados en este mismo shell.
Boris Däppen

2
¿Qué significa el $en IFS=$'\n'?
Ren

2
$hace que los saltos de línea funcionen dentro de la cadena. Esto también debe hacerse local IFS=$'\n'dentro de una función.
Andy Ray

1
@Renn que se $'...'conoce " Cita ANSI-C "
αғsнιη

82

forlos bucles se dividen en cualquier espacio en blanco (espacio, tabulación, nueva línea) de forma predeterminada; la forma más fácil de trabajar en una línea a la vez es usar un while readbucle, que se divide en líneas nuevas:

while read i; do echo "$i"; done < ./file_wget_med

Me gustaría contar con su mando a escupir una palabra por línea (que es lo que pasó cuando lo probé con un archivo de mi propia). Si algo más está sucediendo, no estoy seguro de qué podría estar causándolo.


8
Esta es también una buena alternativa a for i in `ls`; do echo $1; done, canalizando el resultado del comando, mientras que: ls|while read i; do echo $i; done. Esto funciona en nombres de archivos / carpetas con espacios, sin necesidad de cambiar las variables de entorno. Muy buena respuesta.
jishi 01 de

mientras tanto no manejó la primera y última línea muy, no tan bien como el bucle for con IFS
grantbow

@jishi Dado que está diseñado principalmente para la visualización, y responde al tamaño de la ventana de terminal y tal, lsno se considera seguro para su uso con |(el operador de la tubería). findes más flexible además de ser más seguro para este propósito.
SeldomNeedy

3
Esta es una GRAN respuesta, la usé para convertir una lista que tenía en entradas PLIST para una aplicación IOS que estaba desarrollando en mac OSX. Reemplacé la entrada del archivo canalizando el clipboad usando pbpaste ->pbpaste | while read t; do echo "<string>$t</string>"; done
Big Rich

3
ls -1puede usarse con |para garantizar una salida sin formato de una por línea
AndreyS Scherbakov

19
#!/bin/bash
files=`find <subdir> -name '*'`
while read -r fname; do
    echo $fname
done <<< "$files"

Trabajo verificado, no el único revestimiento que probablemente desee, pero no es posible hacerlo con elegancia.


1
una cuerda aquí! más elegante de todas las otras soluciones de la OMI
Eliran Malka

5

Aquí hay una pequeña extensión de la respuesta de Mitchell Currie, que me gusta debido al pequeño alcance de los efectos secundarios, que evita tener que establecer una variable:

#!/bin/bash
while read -r fname; do
    echo $fname
done <<< "`find <subdir>`"

1
Podrías eliminarlo -name '*'y sería lo mismo, ¿no?
wjandrea

1

Lo escribiría así:

cat ./file_wget_med | while read -r j
do
    echo $j
done

ya que requiere cambios mínimos en el script original (excepto para el uso de la solución IFS, pero modifica el bashcomportamiento no solo para esta declaración de control de bucle).


1
Tubería y catson innecesarios aquí. whileel bucle acepta la redirección muy bien, por lo que esto generalmente se escribe comowhile IFS= read -r line; do...done < input.txt
Sergiy Kolodyazhnyy

0

Mapfile es una forma conveniente de leer líneas de un archivo en una matriz indexada, no tan portátil como la lectura, pero un poco más rápido. Al usar for loop evitas crear una subshell.

#!/bin/bash

mapfile -t < file.txt

for line in "${MAPFILE[@]}"; do
    echo $line
done

Tenga en cuenta que cuando use tuberías, colocará el bucle while en una subshell. Los cambios dentro del ciclo while como variables no se propagarán a la parte externa del script.

Ejemplo:

#!/bin/bash

a=0
printf %s\\n {0..5} | while read; do
  ((a++))
done
echo $a # 'a' will always be 0.

(Mejor solución):

#!/bin/bash

b=0
while read; do
  ((b++))
done < <(printf %s\\n {0..5})

echo $b # 'b' equal to 6 (works as expected).

-1

Dandalf se acercó mucho a una solución funcional, ¡pero NUNCA se debería tratar de asignar el resultado de cantidades desconocidas de entrada (es decir find ~/.gdfuse -name '*') a las variables! O, al menos, intente hacer tal cosa a través de una variable de matriz; si insistes en ser tan vago! Entonces, aquí está la solución de Dandalf hecha sin la peligrosa maniobra; y todo en una línea

while read -r fname; do
  echo $fname;
done <<< `find ~/.gdfuse -name '*'

Hola @odoncaoa, intenté hacer el comando de búsqueda sin las comillas dobles, pero hace que todos los resultados aparezcan como una sola línea. Estoy confundido acerca de las "cantidades desconocidas de entrada" con este comando y los peligros involucrados. ¿Puedes dar un ejemplo de los peligros y cómo podrían suceder teóricamente? En serio, no deseo ser flojo al respecto, simplemente me siento más cómodo en otros lenguajes de programación.
Dandalf
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.