Hur slingrar jag över raderna i en fil?

Säg att jag har den här filen:

hello world hello world 

Detta program

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

utgångar

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

Jag skulle vilja ha for iterera över varje rad individuellt utan att ignorera mellanslag, dvs. de två sista raderna bör ersättas med

tester: hello world 

Använd citat for i in "$(cat $1)"; resulterar i att i tilldelas hela filen på en gång. Vad ska jag ändra?

Svar

(9 år senare 🙂
Båda de givna svaren skulle misslyckas på filer utan en ny rad i slutet, detta hoppar effektivt över den sista raden, ger inga fel, skulle leda till katastrof (lärde sig hårt sätt :).

Den bästa koncisa lösningen hittills hittade jag att ”Just Works” (i både bash och sh):

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

För mer fördjupad diskussion se denna StackOverflow-diskussion: Hur man använder ”medan läs ”(Bash) för att läsa den sista raden i en fil om det inte finns någon ny rad i slutet av filen?

Akta dig: den här metoden lägger till en ytterligare ny rad till den sista raden om det finns ingen redan.

Kommentarer

  • Trevlig fångst, tack! Observera " Var uppmärksam på att detta lägger till ytterligare en ny linje vid EOF om det inte redan finns någon. " kommentar dock
  • Tobias, jag ' Jag lägger till detta som en anteckning, tack.

Svar

Med for och IFS :

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

Observera dock att det hoppar över tomma rader som newline är en IFS-blankstegstecken, sekvenser av det räknas som 1 och de främsta och efterföljande ignoreras. Med zsh och ksh93 (inte bash) kan du ändra det till IFS=$"\n\n" för newline som inte ska behandlas speciellt, men observera att alla släp newline-tecken (så att det inkluderar släta tomma rader) alltid tas bort med kommandosubstitutionen.

Eller med read (inte mer cat ):

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

Där bevaras tomma rader, men notera att den skulle hoppa över den sista raden om den inte avgränsades ordentligt av en ny raden. p>

Kommentarer

  • tack, jag visste inte ' jag kunde inte < i en hel slinga. Även om det är helt vettigt nu såg jag det
  • Jag ser IFS \ read -r line' in second example. Is really IFS = `behövs? IMHO är det tillräckligt att säga: while read -r line; do echo "tester: $line"; done < "$1"
  • @GrzegorzWierzowiecki IFS= stänger av borttagning av ledande och efterföljande vitt utrymme. Se I while IFS= read.., varför har IFS ingen effekt?
  • @BenMares För att förhindra globbing uttryck som eventuellt förekommer i texten vi läser från att utvidgas till att matcha filnamn. Försök till exempel printf '%s\n' '*o' 'bar' >afile; touch foo; IFS=$'\n'; for i in $(cat afile); do echo "$i"; done.
  • En while IFS= read -r line || [ "$line" ]; do bearbetar en efterföljande linje som inte avgränsas ordentligt av ett nylinjetecken (men det kommer att läggas tillbaka).

Svar

För vad det är värt måste jag göra det ganska ofta och kommer aldrig ihåg det exakta sättet att använda while IFS= read..., så jag definierade följande funktion i min bash-profil:

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

Denna funktion bestämmer först antalet rader i filen och använder sedan sed för att extrahera rad efter rad och skickar varje rad som ett enda strängargument till en given fungera. Jag antar att det här kan bli riktigt ineffektivt med stora filer, men det har inte varit ett problem för mig hittills (förslag på hur man kan förbättra detta välkomnande naturligtvis).

Användningen är ganska söt 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 

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *