Come eseguire il ciclo sulle righe di un file?

Supponiamo che io abbia questo file:

hello world hello world 

Questo programma

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

output

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

Mi “piacerebbe avere for esegue uniterazione su ciascuna riga singolarmente ignorando gli spazi bianchi, ovvero le ultime due righe devono essere sostituite da

tester: hello world 

Utilizzo di virgolette for i in "$(cat $1)"; fa sì che a i venga assegnato lintero file in una volta. Cosa devo cambiare?

Risposta

(9 anni dopo 🙂
Entrambe le risposte fornite non funzionerebbero sui file senza una nuova riga alla fine, questo salterà effettivamente lultima riga, non produrrà errori, porterebbe al disastro (appreso duramente modo :).

La migliore soluzione concisa che ho trovato finora che “Just Works” (sia in bash che in sh):

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

Per una discussione più approfondita vedi questa discussione StackOverflow: Come usare “mentre read “(Bash) per leggere lultima riga di un file se non cè una nuova riga alla fine del file?

Attenzione: questo approccio aggiunge una nuova riga aggiuntiva allultima riga se cè nessuno già.

Commenti

  • Bella cattura, grazie! Tieni presente il " Fai attenzione che in questo modo viene aggiunta una nuova riga aggiuntiva a EOF se non ce nè già nessuna. " comment anche se
  • Tobias, io ' aggiungerò questo come nota, grazie.

Risposta

Con for e IFS :

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

Nota tuttavia che salterà le righe vuote poiché nuova riga è un carattere IFS-spazio bianco, sequenze di esso conta come 1 e quelli iniziali e finali vengono ignorati. Con zsh e ksh93 (non bash), puoi cambiarlo in IFS=$"\n\n" per non trattare in modo speciale la nuova riga, tuttavia si noti che tutti i caratteri finali di nuova riga (in modo che includa le righe vuote finali) verranno sempre rimossi dalla sostituzione del comando.

Oppure con read (non più cat ):

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

Lì, le righe vuote vengono conservate, ma nota che salterebbe lultima riga se non fosse correttamente delimitata da un carattere di nuova riga.

Commenti

  • grazie, ' non sapevo che si potesse < in un intero ciclo. Anche se ha perfettamente senso ora che lho visto
  • Vedo IFS \ read -r line' in second example. Is really IFS = `necessario? IMHO è sufficiente dire: while read -r line; do echo "tester: $line"; done < "$1"
  • @GrzegorzWierzowiecki IFS= disattiva la rimozione degli spazi bianchi iniziali e finali. Vedi In while IFS= read.., perché IFS non ha alcun effetto?
  • @BenMares Per prevenire il globbing espressioni che potrebbero apparire nel testo che stiamo leggendo dallessere espanse ai nomi di file corrispondenti. Prova, ad esempio, 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 elaborerà una riga finale non correttamente delimitata da un carattere di nuova riga (ma verrà aggiunto di nuovo).

Risposta

Per quello che vale, devo fare abbastanza spesso, e non riesco mai a ricordare il modo esatto di usare while IFS= read..., quindi ho definito la seguente funzione nel mio profilo 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 } 

Questa funzione determina prima il numero di righe nel file, quindi utilizza sed per estrarre riga dopo riga e passa ogni riga come un singolo argomento stringa a un dato funzione. Suppongo che questo potrebbe diventare davvero inefficiente con file di grandi dimensioni, ma finora non è stato un problema per me (suggerimenti su come migliorare questa accoglienza ovviamente).

Luso è piuttosto dolce IMO:

>> 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 

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *