È 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, ilread
Il comando invia un segnale di successo alwhile
, e ilwhile
loop passa il flusso di esecuzione al corpo del ciclo. Nota che ilread
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 luiread
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 awhile
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.