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