Una ventana de terminal en un sistema informático Linux.

Cómo procesar un archivo línea por línea en un script Bash de Linux

Fatmawati Achmad Zaenuri / Shutterstock

Leer el contenido de un archivo de texto de Linux línea por línea en un script de shell es bastante fácil, siempre que maneje algunos errores sutiles. He aquí cómo hacerlo de forma segura.

Archivos, texto y modismos

Cada lenguaje de programación tiene un conjunto de modismos. Estas son las formas estándar y sencillas de realizar un montón de tareas comunes. Esta es la forma básica o predeterminada de usar una de las características del lenguaje con el que está trabajando el programador. Son parte del conjunto de herramientas de un programador de planes mentales.

Acciones como leer datos de archivos, usar bucles e intercambiar los valores de dos variables son buenos ejemplos. El programador conocerá al menos una forma de lograr sus objetivos de forma genérica o básica. Quizás esto sea suficiente para el requisito que nos ocupa. O tal vez arreglen el código para hacerlo más eficiente o aplicable a la solución específica que están desarrollando. Pero tener a mano el modismo básico es un buen punto de partida.

Conocer y comprender los modismos de un idioma también facilita el aprendizaje de un nuevo lenguaje de programación. Savoir comment les choses sont construites dans un langage et rechercher l’équivalent – ou la chose la plus proche – dans un autre langage est un bon moyen d’apprécier les similitudes et les différences entre les langages de programmation que vous connaissez déjà et celui que usted aprende.

Leer líneas de un archivo: la única línea

En Bash, puedes usar un while recorrer la línea de comando para leer cada línea de texto en un archivo y hacer algo con él. Nuestro archivo de texto se llama «data.txt». Contiene una lista de los meses del año.

January
February
March
.
.
October
November
December

Nuestro forro simple es:

while read line; do echo $line; done < data.txt

la while loop lee una línea del archivo y el flujo de ejecución del pequeño programa pasa al cuerpo del loop. la echo El comando escribe la línea de texto en la ventana de la terminal. El intento de lectura falla cuando no hay más líneas para leer y el ciclo está completo.

Un truco interesante es la capacidad de redirigir un archivo en un bucle. En otros lenguajes de programación, deberá abrir el archivo, leerlo y volver a cerrarlo cuando haya terminado. Con Bash, puede usar la redirección de archivos y dejar que el shell maneje todas esas cosas de bajo nivel por usted.

Por supuesto, esta frase no es muy útil. Linux ya proporciona cat comando, que hace exactamente eso por nosotros. Hemos creado un método a largo plazo para reemplazar un comando de tres letras. Pero demuestra visiblemente los principios de la lectura de un archivo.

Funciona bastante bien, hasta cierto punto. Supongamos que tenemos otro archivo de texto que contiene los nombres de los meses. En este archivo, la secuencia de escape para un carácter de nueva línea se ha agregado a cada línea. Lo llamaremos «data2.txt».

Januaryn
Februaryn
Marchn
.
.
Octobern
Novembern
Decembern

Usemos nuestro one-liner en nuestro nuevo archivo.

while read line; do echo $line; done < data2.txt

El carácter de escape de barra invertida » «fue rechazado. El resultado es que se agregó una» n «a cada línea. Bash interpreta la barra invertida como el comienzo de una secuencia de escape. A menudo, no queremos que Bash interprete lo que está leyendo. Puede ser más conveniente leer una línea completa (secuencias de escape con barra invertida y todo) y elegir qué analizar o reemplazar usted mismo, en su propio código.

Si queremos realizar un procesamiento o análisis significativo en líneas de texto, tendremos que utilizar un script.

Leer líneas de un archivo con un script

Aquí está nuestro escenario. Se llama «script1.sh».

#!/bin/bash

Counter=0

while IFS='' read -r LinefromFile || [[ -n "${LinefromFile}" ]]; do

    ((Counter++))
    echo "Accessing line $Counter: ${LinefromFile}"

done < "$1"

Definimos una variable llamada Counter a cero, luego configuramos nuestro while lazo.

La primera declaración en la línea while es IFS='' . IFS significa separador de campo interno. Contiene valores que Bash usa para identificar los límites de las palabras. De forma predeterminada, el comando de lectura elimina los espacios en blanco iniciales y finales. Si queremos leer las líneas del archivo exactamente como son, necesitamos definir IFS ser una cadena vacía.

Podríamos establecer esto una vez fuera del ciclo, al igual que establecemos el valor de Counter . Pero con scripts más complejos, especialmente aquellos que contienen muchas funciones definidas por el usuario, es posible que IFS se puede establecer en diferentes valores en otra parte del script. Asegurarse de que IFS se establece en una cadena vacía siempre que el while El bucle itera asegura que sepamos cuál será su comportamiento.

Vamos a leer una línea de texto en una variable llamada LinefromFile . Usamos el -r (lea la barra invertida como un carácter normal) para ignorar las barras invertidas. Serán tratados como cualquier otro personaje y no recibirán ningún trato especial.

Hay dos condiciones que satisfarán while bucles y permite que el texto sea procesado por el cuerpo del bucle:

  • read -r LinefromFile : Cuando se lee correctamente una línea de texto del archivo, el read El comando envía una señal de éxito al while , y el while loop pasa el flujo de ejecución al cuerpo del loop. Tenga en cuenta que el read el comando debe ver un carácter de nueva línea al final de la línea de texto para contarlo como una lectura exitosa. Si el archivo no es un POSIX archivo de texto compatible, el la última línea no puede incluir un carácter de nueva línea. Si la read el comando ve el marcador de fin de archivo (EOF) antes de que la línea termine con una nueva línea, no la tratará como una lectura exitosa. Si esto sucede, la última línea de texto no se pasará al cuerpo del bucle y no se procesará.
  • [ -n "${LinefromFile}" ] : Necesitamos hacer un trabajo adicional para manejar archivos que no son compatibles con POSIX. Esta comparación verifica el texto leído del archivo. Si no termina con un carácter de nueva línea, esta comparación siempre devolverá el éxito al while lazo. Esto asegura que todos los fragmentos de la línea final sean procesados ​​por el cuerpo del bucle.

Estas dos cláusulas están separadas por el operador lógico OR » || ”De modo que si una de las cláusulas devuelve éxito, el texto recuperado es procesado por el cuerpo del bucle, haya o no un carácter de nueva línea.

En el cuerpo de nuestro bucle, incrementamos el Counter variable por uno y usando echo para enviar salida a la ventana del terminal. Se muestran el número de línea y el texto de cada línea.

Siempre podemos usar nuestro truco de redireccionamiento para redirigir un archivo en un bucle. En este caso, estamos redirigiendo $ 1, una variable que contiene el nombre del primer parámetro de línea de comando pasado al script. Con este truco, podemos pasar fácilmente el nombre del archivo de datos en el que queremos que trabaje el script.

Copie y pegue el script en un editor y guárdelo con el nombre de archivo «script1.sh». Utilizar el chmod orden para hacerlo ejecutable.

chmod +x script1.sh

Veamos qué hace nuestro script con el archivo de texto data2.txt y las barras invertidas que contiene.

./script1.sh data2.txt

Cada carácter de la línea se muestra literalmente. Las barras invertidas no se interpretan como caracteres de escape. Están impresos en caracteres normales.

Pasar la línea a una función

Seguimos repitiendo el texto en la pantalla. En un escenario de programación real, probablemente estaríamos a punto de hacer algo más interesante con la línea de texto. En la mayoría de los casos, es una buena práctica de programación manejar el procesamiento posterior de la línea en otra función.

Así es como podríamos hacerlo. Es «script2.sh».

#!/bin/bash

Counter=0

function process_line() {

    echo "Processing line $Counter: $1"

}

while IFS='' read -r LinefromFile || [[ -n "${LinefromFile}" ]]; do

    ((Counter++))
    process_line "$LinefromFile"

done < "$1"

Definimos nuestro Counter variable como antes, entonces definimos una función llamada process_line() . La definición de una función debe aparecer antes de que se llame a la función por primera vez en el script.

Nuestra función pasará la nueva línea de texto leído en cada iteración del while lazo. Podemos acceder a este valor en la función usando el $1 variable. Si hubieran pasado dos variables a la función, podríamos acceder a esos valores usando $1 y $2 y así sucesivamente para más variables.

La While el bucle es esencialmente el mismo. Solo hay un cambio dentro del cuerpo de la hebilla. la echo la línea ha sido reemplazada por una llamada a process_line() Una función. Tenga en cuenta que no es necesario utilizar corchetes «()» en el nombre de la función cuando la llame.

El nombre de la variable que contiene la línea de texto, LinefromFile , se incluye entre comillas cuando se pasa a la función. Esto es para líneas que contienen espacios. Sin comillas, la primera palabra se trata como $1 por la función, la segunda palabra se considera como $2 etc. El uso de comillas garantiza que toda la línea de texto se trate, como un todo, como $1. Cuidado, no es lo mismo $1 que contiene el mismo archivo de datos pasado al script.

Porque Counter se declaró en el cuerpo principal del script y no en una función, se puede hacer referencia a él en el process_line() Una función.

Copie o escriba el script anterior en un editor y guárdelo con el nombre de archivo «script2.sh». Hágalo ejecutable con chmod :

chmod +x script2.sh

Ahora podemos ejecutarlo y transmitir un nuevo archivo de datos, «data3.txt». Contiene una lista de meses y una fila con muchas palabras.

January
February
March
.
.
October
November nMore text "at the end of the line"
December

Nuestro pedido es:

./script2.sh data3.txt

Las líneas se leen del archivo y se pasan una por una al process_line() Una función. Todas las líneas se muestran correctamente, incluido el intruso con retroceso, comillas y varias palabras.

Los bloques de construcción son útiles

Hay una escuela de pensamiento que dice que un idioma debe contener algo exclusivo de ese idioma. No es una creencia a la que me suscriba. Lo importante es que hace un buen uso del lenguaje, es fácil de recordar y proporciona una forma confiable y sólida de implementar ciertas funciones en su código.

Deja un comentario

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

Experto Geek - Tu Guía en Tendencias Tecnológicas