Cómo leer un archivo línea a línea en Bash
En esta entrada aprendemos y refrescamos el conocimiento de leer archivos línea a línea, cuando elaboramos un script o guion en Bash. Cabe decir que es una nota para mis apuntes, ya que, con tantas cosas en la cabeza, me las tengo que apuntar en el algún lado, y que mejor que este humilde blog. Así que, sin más preámbulo, vamos al turrón.
Leer un archivo línea a línea en Bash
Veamos primero la sintaxis habitual. Esta consiste en leer cada línea de la siguiente manera:
while IFS= read -r line
do
echo "$line"
done < input_file
O bien, podemos hacer lo mismo en una sola línea:
while IFS= read -r line; do echo $line; done < input_file
¿Qué hemos hecho?
El archivo de entrada («input_file») es el nombre del archivo que deseamos que esté abierto para su lectura mediante el comando «read». Dicho comando lee el archivo línea por línea, asignando cada línea a la variable de línea. Una vez que se procesan todas las líneas, el bucle «while» terminará. El separador de campo interno (IFS) se establece en la cadena nula para conservar los espacios en blanco iniciales y finales, que es el comportamiento predeterminado del comando de lectura (read)
Ejemplos para leer un archivo línea a línea
Veamos el siguiente ejemplo. Supongamos que tenemos un archivo llamado distros.txt que contiene una lista de algunas de las distribuciones de GNU/Linux más populares y sus gestores de paquetes separados con coma (,) :
distros.txt
Ubuntu,apt
Debian,apt
CentOS,yum
Arch Linux,pacman
Fedora,dnf
Para leer el archivo línea por línea, ejecutaremos el siguiente código en su terminal:
while IFS= read -r line
do
echo "$line"
done < distros.txt
El código leerá el archivo por línea, asignará la línea a una variable y hará eco de la variable. Básicamente veríamos la misma salida como si mostrara el contenido del archivo con el comando cat.
¿Qué sucede si deseamos imprimir solo las distribuciones que usan apt? Una forma sería verificar si la línea contiene la subcadena «apt», como sigue:
while IFS= read -r line
do
if [[ "$line" == *"apt"* ]]; then
echo "$line"
fi
done < distros.txt
Con la salida:
Ubuntu,apt
Debian,apt
Al leer el archivo línea por línea, también podemos pasar más de una variable al comando de lectura, que dividirá la línea en campos basados en el IFS. El primer campo se asigna a la primera variable, el segundo a la segunda variable, y así sucesivamente. Si hay más campos que variables, los campos restantes se asignan a la última variable.
En el siguiente ejemplo, estamos configurando IFS a una coma (,) y pasando dos variables «distro» y «pm» al comando de lectura. Todo desde el principio de la línea hasta la primera coma se asignará a la primera variable (distro) y el resto de la línea se asignará a la segunda variable (pm):
while IFS=, read -r distro pm
do
echo "$pm" is the package manager for "$distro"
done < distros.txt
Con la salida:
apt is the package manager for Ubuntu
apt is the package manager for Debian
yum is the package manager for CentOS
pacman is the package manager for Arch Linux
dnf is the package manager for Fedora
Métodos alternativos de lectura de archivos
Usando una sustitución de proceso
La sustitución de procesos nos permite pasar la salida del comando como un nombre de archivo:
while IFS= read -r line
do
echo "$line"
done < <(cat input_file )
Usando un string
Aquí el valor «string» es una variante del documento. La cadena (cat input_file) mantendrá las nuevas líneas:
while IFS= read -r line
do
echo "$line"
done <<< $(cat input_file )
Usando el descriptor de archivo
También podemos proporcionar la entrada al bucle utilizando un descriptor de archivo:
while IFS= read -r -u9 line
do
echo "$line"
done 9< input_file
Cuando trabajemos con descriptores de archivos, debemos utilizar un número entre 4 y 9 para evitar conflictos con los descriptores de archivos internos del shell.
Conclusión
En Bash, podemos leer un archivo línea por línea al proporcionar el nombre del archivo como una entrada para un ciclo de tiempo de lectura.
Respecto a las fuentes consultadas
El artículo es una adaptación de la siguiente entrada: