Come elaborare un file riga per riga in uno script bash di Linux

Contenuti

Una finestra di terminale su un sistema informatico Linux.

È abbastanza facile leggere il contenuto di un file di testo Linux riga per riga in uno script di shell, fintanto che affronti dei problemi sottili. Ecco come farlo in sicurezza.

record, testo e modi di dire

Ogni linguaggio di programmazione ha una serie di idiomi. Questi sono i modi standard e semplici per eseguire una serie di attività comuni. Sono il modo elementare o predefinito di utilizzare una delle caratteristiche del linguaggio con cui sta lavorando il programmatore. Diventano parte del toolkit di un programmatore di progetti mentali.

Azioni come leggere i dati dai file, lavorare con i loop e scambiare i valori di due variabili sono buoni esempi. Il programmatore conoscerà almeno un modo per raggiungere i propri obiettivi in ​​modo generico o di base. Forse questo è sufficiente per il requisito a portata di mano.. O forse abbelliranno il codice per renderlo più efficiente o applicabile alla risposta specifica che stanno sviluppando. Ma avere la lingua di base a portata di mano è un ottimo punto di partenza.

Conoscere e comprendere idiomi in un linguaggio facilita anche l'acquisizione di un nuovo linguaggio di programmazione. Sapere come sono costruite le cose in un linguaggio e cercare il simile - o il più vicino - in un altro linguaggio è un buon modo per apprezzare le somiglianze e le differenze tra i linguaggi di programmazione che già conosci e quello che stai imparando..

Leggere righe da un file: una linea

in bash, È possibile utilizzare un while loop sulla riga di comando per leggere ogni riga di testo in un file e fare qualcosa con esso. Il nostro file di testo si chiama “data.txt”. Contiene un elenco di mesi dell'anno.

January
February
March
.
.
October
November
December

Il nostro singolo a riga singola è:

mentre leggi la riga; fai echo $line; fatto < data.txt

il while loop legge una riga dal file, e il flusso di esecuzione del programmino passa al corpo del ciclo. il echo comando scrive la riga di testo nella finestra del terminale. Il tentativo di lettura fallisce quando non ci sono più righe da leggere e il ciclo è terminato.

Un bel trucco è la capacità di reindirizzare un file in un ciclo. In altri linguaggi di programmazione, avrei bisogno di aprire il file, leggilo e chiudilo di nuovo quando hai finito. Con Bash, puoi semplicemente usare il reindirizzamento dei file e lasciare che la shell gestisca tutte quelle cose di basso livello per te.

Certo, questa frase non è molto utile. Linux fornisce già cat comando, cosa fa esattamente per noi?. Abbiamo creato una lunga strada per sostituire un comando di tre lettere. Ma dimostra visibilmente i principi della lettura di un file.

Funziona abbastanza bene, fino a un certo punto. Supponiamo di avere un altro file di testo che contiene i nomi dei mesi. In questo file, La sequenza di escape per un carattere newline è stata aggiunta a ogni riga. Ti chiameremo “dati2.txt”.

Januaryn
Februaryn
Marchn
.
.
Octobern
Novembern
Decembern

Usiamo il nostro one-liner nel nostro nuovo archivio.

mentre leggi la riga; fai echo $line; fatto < dati2.txt

Il carattere di escape barra rovesciata ” "è stato scartato. Il risultato è che a “n” ad ogni riga. Bash interpreta la barra rovesciata come l'inizio di a sequenza di fuga. Spesso, non vogliamo che Bash interpreti ciò che sta leggendo. Potrebbe essere più comodo leggere una riga nella sua interezza (sequenze di escape con barra rovesciata e tutto il resto) e seleziona cosa analizzare o sostituire te stesso, all'interno del tuo codice.

Se vogliamo fare qualche elaborazione o analisi significativa delle righe di testo, dovremo usare uno script.

Leggere righe da un file con uno script

Ecco la nostra sceneggiatura. È chiamato “script1.sh”.

#!/bin/bash

Counter=0

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

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

done < "$1"

Impostiamo una variabile chiamata Counter acciaio, allora definiamo il nostro while cerchio.

La prima affermazione nella riga while è IFS='' . IFS significa separatore di campo interno. Contiene valori che Bash usa per identificare i confini delle parole. Per impostazione predefinita, il comando read rimuove gli spazi iniziali e finali. Se vogliamo leggere le righe del file esattamente come sono, dobbiamo configurare IFS essere una stringa vuota.

Potremmo determinarlo una volta fuori dal giro, nello stesso modo in cui impostiamo il valore di Counter . Ma con script più complessi, specialmente quelli con molte funzioni definite dall'utente al loro interno, è fattibile che IFS potrebbe essere impostato su valori diversi altrove nello script. Garantire che IFS è impostato su una stringa vuota ogni volta che while Il ciclo itera assicura che sappiamo quale sarà il suo comportamento.

Leggeremo una riga di testo in una variabile chiamata LinefromFile . Stiamo usando il -r (leggi la barra rovesciata come un carattere normale) ignorare le barre rovesciate. Verranno trattati come qualsiasi altro personaggio e non riceveranno alcun trattamento speciale.

Ci sono due condizioni che soddisferanno il while loop e consente al testo di essere elaborato dal corpo del loop:

  • read -r LinefromFile : Quando una riga di testo viene letta correttamente dal file, il read Il comando invia un segnale di successo al while , e il while loop passa il flusso di esecuzione al corpo del ciclo. Nota che il read Il comando deve vedere a carattere di nuova riga alla fine della riga di testo per considerarla una lettura riuscita. Se il file non è a POSIX file di testo compatibile, il l'ultima riga potrebbe non includere un nuovo carattere di riga. Se lui read comando vedere il marcatore di fine file (EOF) prima che la riga termini con una nuova riga, no trattalo come una lettura di successo. Se ciò accade, l'ultima riga di testo non verrà passata al corpo del ciclo e non verrà resa.
  • [ -n "${LinefromFile}" ] : Abbiamo bisogno di fare del lavoro aggiuntivo per gestire file non conformi a POSIX. Questo confronto controlla il testo letto dal file. Se non termina con un carattere di nuova riga, questo confronto restituirà ancora successo a while cerchio. Ciò garantisce che qualsiasi frammento di linea finale venga elaborato dal corpo del ciclo.

Queste due clausole sono separate dall'operatore logico OR ” || “Quindi se qualunque La clausola restituisce successo, il testo recuperato viene elaborato dal corpo del ciclo, se c'è o meno un carattere di nuova riga.

Nel corpo del nostro loop, stiamo aumentando il Counter variabile di uno e usando echo per inviare un output alla finestra del terminale. Vengono visualizzati il ​​numero di riga e il testo per ogni riga.

Possiamo ancora usare il nostro trucco di reindirizzamento per reindirizzare un file a un ciclo. Per questo caso, stiamo reindirizzando $ 1, una variabile contenente il nome del primo parametro della riga di comando passato allo script. Con questo trucco, possiamo passare facilmente il nome del file di dati su cui vogliamo che lo script lavori.

Copia e incolla lo script in un editor e salvalo con il nome del file “script1.sh”. Utilizzare il chmod comando per renderlo eseguibile.

chmod +x script1.sh

Vediamo cosa fa il nostro script con il file di testo data2.txt e le barre rovesciate che contiene.

./script1.sh data2.txt

Ogni carattere sulla riga viene visualizzato letteralmente. Le barre rovesciate non vengono interpretate come caratteri di escape. Sono stampati come caratteri normali.

Passa la linea a una funzione

Stiamo ancora facendo eco al testo sullo schermo. In uno scenario di programmazione del mondo reale, probabilmente saremmo in procinto di fare qualcosa di più interessante con la riga di testo. Nella maggior parte dei casi, è buona pratica di programmazione gestire la post-elaborazione della linea in un'altra funzione.

Ecco come potremmo farlo. Questo è “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"

Definiamo il nostro Counter variabile come prima, e poi definiamo una funzione chiamata process_line() . Dovrebbe apparire la definizione di una funzione prima la funzione viene chiamata per prima nello script.

Alla nostra funzione verrà passata la riga di testo appena letta ad ogni iterazione del while cerchio. Possiamo accedere a quel valore all'interno della funzione usando il $1 variabile. Se avessero passato due variabili alla funzione, potremmo accedere a quei valori usando $1 e $2 e così via per più variabili.

Leggehile il ciclo è per lo più lo stesso. C'è solo un cambiamento all'interno del corpo del ciclo. il echo La linea è stata sostituita da una chiamata a process_line() funzione. Nota che non è necessario utilizzare le parentesi "()"Nel nome della funzione quando la chiami.

Il nome della variabile che contiene la riga di testo, LinefromFile , è racchiuso tra virgolette quando passato alla funzione. Questo si adatta alle linee che contengono spazi. Senza le virgolette, la prima parola è trattata come $1 per funzione, la seconda parola è considerata $2 , e così via. L'uso delle virgolette garantisce la gestione dell'intera riga di testo, su tutto, Che cosa $1. Si prega di notare che questo è no lo stesso $1 contenente lo stesso file di dati passato allo script.

Perché Counter è stato dichiarato nel corpo principale dello script e non all'interno di una funzione, può essere referenziato all'interno del process_line() funzione.

Copia o scrivi lo script sopra in un editor e salvalo con il nome del file “script2.sh”. Rendilo eseguibile con chmod :

chmod +x script2.sh

Ora possiamo eseguirlo e passare un nuovo file di dati, “dati3.txt”. Questo ha un elenco dei mesi e una riga con molte parole.

January
February
March
.
.
October
November nMore text "alla fine della riga"
Dicembre

Il nostro comando è:

./script2.sh data3.txt

Le righe vengono lette dal file e passate una ad una al process_line() funzione. Tutte le linee sono visualizzate correttamente, compreso quello con il rinculo, citazioni e più parole.

I blocchi di costruzione sono utili

C'è una linea di pensiero che dice che una lingua deve contenere qualcosa di unico per quella lingua. Non è una convinzione a cui sottoscrivo. L'importante è che faccia buon uso del linguaggio, è facile da ricordare e fornisce un modo affidabile e robusto per implementare alcune funzioni nel codice.

Iscriviti alla nostra Newsletter

Non ti invieremo posta SPAM. Lo odiamo quanto te.