Spune că am acest fișier:
hello world hello world
Acest program
#!/bin/bash for i in $(cat $1); do echo "tester: $i" done
ieșiri
tester: hello tester: world tester: hello tester: world
Aș dori să am for
iterați peste fiecare linie, ignorând în mod individual spațiile albe, de exemplu, ultimele două linii ar trebui înlocuite cu
tester: hello world
Folosind ghilimele
are ca rezultat i
a fi atribuit întregul fișier simultan. Ce ar trebui să schimb?
Răspuns
(9 ani mai târziu 🙂
Ambele răspunsuri furnizate ar eșua pe fișiere fără o linie nouă la sfârșit, acest lucru va sări peste ultima linie, nu va produce erori, ar duce la dezastru (învățat greu mod :).
Cea mai bună soluție concisă pe care am găsit-o până acum că „Just Works” (atât în bash, cât și în sh):
while IFS="" read -r LINE || [ -n "${LINE}" ]; do echo "processing line: ${LINE}" done < /path/to/input/file.txt
Pentru o discuție mai aprofundată, consultați această discuție StackOverflow: Cum se folosește „în timp ce citiți „(Bash) pentru a citi ultima linie dintr-un fișier dacă nu există nicio linie nouă la sfârșitul fișierului?
Atenție: această abordare adaugă o nouă linie suplimentară la ultima linie dacă există niciunul deja.
Comentarii
- O captură frumoasă, mulțumesc! Rețineți " Feriți-vă că acest lucru adaugă o nouă linie suplimentară la EOF dacă nu există deja. " comentariu deși
- Tobias, ' voi adăuga acest lucru ca notă, mulțumesc.
Răspuns
Cu for
și IFS :
#!/bin/bash IFS=$"\n" # make newlines the only separator set -f # disable globbing for i in $(cat < "$1"); do echo "tester: $i" done
Rețineți totuși că va sări peste liniile goale ca newline fiind un caracter IFS-white-space, secvențe din acesta contează ca 1, iar cele din urmă și cele din urmă sunt ignorate. Cu zsh
și ksh93
(nu bash
), îl puteți schimba în IFS=$"\n\n"
pentru ca noua linie să nu fie tratată special, totuși rețineți că toate caracterele de urmărire de linie nouă (astfel încât să includă și linii goale) vor fi întotdeauna eliminate prin substituirea comenzii.
Sau cu read
(nu mai există cat
):
#!/bin/bash while IFS= read -r line; do echo "tester: $line" done < "$1"
Acolo se păstrează liniile goale, dar rețineți că s-ar sări ultima linie dacă nu ar fi delimitată corect de un caracter de linie nouă.
Comentarii
- mulțumesc, nu ' nu știam că se poate
<
într-o buclă întreagă. Deși are perfect sens acum l-am văzut - Văd
IFS \ read -r line' in second example. Is really
IFS = `este necesar? Îmi este suficient să spunem:while read -r line; do echo "tester: $line"; done < "$1"
- @GrzegorzWierzowiecki
IFS=
dezactivează dezizolarea spațiului alb principal. A se vedea Înwhile IFS= read..
, de ce IFS nu are efect? - @BenMares Pentru a preveni aglomerarea expresii care apar posibil în textul pe care îl citim de la extindere la nume de fișiere potrivite. Încercați, de exemplu,
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
va procesa o linie finală delimitată în mod corespunzător de un caracter de linie nouă (dar va fi adăugat înapoi).
Răspuns
Pentru ceea ce merită, trebuie să fac asta destul de des și nu-mi amintesc niciodată modul exact de utilizare a while IFS= read...
, așa că am definit următoarea funcție în profilul meu 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 }
Această funcție determină mai întâi numărul de linii din fișier, apoi folosește sed
pentru a extrage linie după linie și trece fiecare linie ca un singur șir argument oricărui dat funcţie. Presupun că acest lucru ar putea deveni cu adevărat ineficient cu fișierele mari, dar asta nu a fost o problemă pentru mine până acum (sugestii despre cum să îmbunătățesc această întâmpinare bineînțeles).
Utilizarea este destul de dulce 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