¿Cómo recorrer las líneas de un archivo?

Digamos que tengo este archivo:

hello world hello world 

Este programa

#!/bin/bash for i in $(cat $1); do echo "tester: $i" done 

salidas

tester: hello tester: world tester: hello tester: world 

Me gustaría tener el for itera sobre cada línea individualmente ignorando los espacios en blanco, es decir, las dos últimas líneas deben ser reemplazadas por

tester: hello world 

Usando comillas for i in "$(cat $1)"; da como resultado que i se le asigne todo el archivo de una vez. ¿Qué debo cambiar?

Respuesta

(9 años después 🙂
Ambas respuestas proporcionadas fallarían en archivos sin una nueva línea al final, esto efectivamente omitirá la última línea, no producirá errores, conduciría a un desastre (se aprendió mucho manera :).

La mejor solución concisa que encontré hasta ahora que «Simplemente funciona» (tanto en bash como en sh):

while IFS="" read -r LINE || [ -n "${LINE}" ]; do echo "processing line: ${LINE}" done < /path/to/input/file.txt 

Para una discusión más profunda, vea esta discusión de StackOverflow: Cómo usar «while read «(Bash) para leer la última línea en un archivo si no hay una nueva línea al final del archivo?

Cuidado: este enfoque agrega una nueva línea adicional a la última línea si hay ninguno ya.

Comentarios

  • Buena captura, gracias! Tenga en cuenta el " Tenga en cuenta que esto agrega una nueva línea adicional en EOF si no hay ninguna. " comenta aunque
  • Tobias, ' agregaré esto como una nota, gracias.

Responder

Con for y IFS :

#!/bin/bash IFS=$"\n" # make newlines the only separator set -f # disable globbing for i in $(cat < "$1"); do echo "tester: $i" done 

Sin embargo, tenga en cuenta que omitirá las líneas vacías ya que nueva línea es un carácter IFS-espacio en blanco, secuencias de ellos cuentan como 1 y los primeros y finales se ignoran. Con zsh y ksh93 (no bash), puedes cambiarlo a IFS=$"\n\n" para que la nueva línea no se trate de manera especial, sin embargo, tenga en cuenta que todos los caracteres finales de nueva línea (por lo que incluyen líneas vacías finales) siempre se eliminarán mediante la sustitución del comando.

O con read (no más cat ):

#!/bin/bash while IFS= read -r line; do echo "tester: $line" done < "$1" 

Allí, las líneas vacías se conservan, pero tenga en cuenta que se omitiría la última línea si no estuviera delimitada correctamente por un carácter de nueva línea.

Comentarios

  • gracias, no ' sabía que uno podía < en un ciclo completo. Aunque ahora tiene mucho sentido, lo vi
  • Veo IFS \ read -r line' in second example. Is really ¿Se necesita IFS = `? En mi humilde opinión, basta con decir: while read -r line; do echo "tester: $line"; done < "$1"
  • @GrzegorzWierzowiecki IFS= desactiva la eliminación de los espacios en blanco iniciales y finales. Consulte En while IFS= read.., ¿por qué IFS no tiene ningún efecto?
  • @BenMares Para evitar el globbing las expresiones que posiblemente aparecen en el texto que estamos leyendo se expanden a nombres de archivo coincidentes. Pruebe, por ejemplo, printf '%s\n' '*o' 'bar' >afile; touch foo; IFS=$'\n'; for i in $(cat afile); do echo "$i"; done.
  • Un while IFS= read -r line || [ "$line" ]; do procesará una línea final que no esté delimitada correctamente por un carácter de nueva línea (pero se volverá a agregar).

Responder

Por lo que vale, necesito hacer eso con bastante frecuencia, y nunca puedo recordar la forma exacta de usar while IFS= read..., así que definí la siguiente función en mi perfil de bash:

# iterate the line of a file and call input function iterlines() { (( $# < 2 )) && { echo "Usage: iterlines <File> <Callback>"; return; } local File=$1 local Func=$2 n=$(cat "$File" | wc -l) for (( i=1; i<=n; i++ )); do "$Func" "$(sed "${i}q;d" "$File")" done } 

Esta función primero determina el número de líneas en el archivo, luego usa sed para extraer línea tras línea y pasa cada línea como un argumento de cadena única a cualquier función. Supongo que esto puede volverse realmente ineficiente con archivos grandes, pero eso no ha sido un problema para mí hasta ahora (sugerencias sobre cómo mejorar esta bienvenida, por supuesto).

El uso es bastante bueno OMI:

>> cat example.txt # note the use of spaces, whitespace, etc. a/path This is a sentence. "wi\th quotes" $End >> iterlines example.txt echo # preserves quotes, $ and whitespace a/path This is a sentence. "wi\th quotes" $End >> x() { echo "$#"; }; iterlines example.txt x # line always passed as single input string 1 1 1 1 1 

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *