Hvordan løber man over linjerne i en fil?

Sig, at jeg har denne fil:

hello world hello world 

Dette program

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

output

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

Jeg vil gerne have for gentages over hver linje individuelt og ignorerer dog mellemrum, dvs. de to sidste linjer skal erstattes af

tester: hello world 

Brug af anførselstegn for i in "$(cat $1)"; resulterer i, at i tildeles hele filen på én gang. Hvad skal jeg ændre?

Svar

(9 år senere 🙂
Begge forudsatte svar ville mislykkes på filer uden en ny linje i slutningen, dette springer effektivt over den sidste linje, giver ingen fejl, vil føre til katastrofe (lært hårdt måde :).

Den bedste koncise løsning, jeg hidtil har fundet, at “Just Works” (i både bash og sh):

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

Se denne StackOverflow-diskussion for mere dybtgående diskussion: Sådan bruges “mens læs “(Bash) for at læse den sidste linje i en fil, hvis der ikke er nogen ny linje i slutningen af filen?

Pas på: denne tilgang tilføjer en ekstra ny linje til den sidste linje, hvis der er ingen allerede.

Kommentarer

  • Dejlig fangst, tak! Bemærk " Pas på, at dette tilføjer en ekstra ny linje ved EOF, hvis der ikke allerede er nogen. " kommentar dog
  • Tobias, jeg ' Jeg tilføjer dette som en note, tak.

Svar

Med for og IFS :

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

Bemærk dog, at det springer tomme linjer over, da newline er et IFS-hvidt mellemrumstegn, sekvenser af det tæller som 1, og de førende og bageste ignoreres. Med zsh og ksh93 (ikke bash) kan du ændre det til IFS=$"\n\n" for newline, der ikke skal behandles specielt, dog bemærk at alle trail newline-tegn (så det inkluderer tomme linier) altid fjernes ved kommandosubstitution.

Eller med read (ikke mere cat ):

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

Der bevares tomme linjer, men bemærk, at det springer den sidste linje over, hvis den ikke blev afgrænset korrekt med et nyt linjetegn.

Kommentarer

  • tak, jeg vidste ikke ' jeg kunne ikke < i en hel løkke. Selvom det giver perfekt mening nu så jeg det
  • Jeg ser IFS \ read -r line' in second example. Is really IFS = `nødvendigt? IMHO er det nok at sige: while read -r line; do echo "tester: $line"; done < "$1"
  • @GrzegorzWierzowiecki IFS= slukker for fjernelse af ledende og efterfølgende hvidt rum. Se I while IFS= read.., hvorfor har IFS ingen effekt?
  • @BenMares For at forhindre globbing udtryk, der muligvis vises i teksten, vi læser, fra at blive udvidet til matchende filnavne. Prøv f.eks. 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 behandler en efterfølgende linje, der ikke er korrekt afgrænset af et nyt linjetegn (men det tilføjes tilbage).

Svar

For hvad det er værd, skal jeg gøre det ganske ofte og kan aldrig huske den nøjagtige måde at bruge while IFS= read... på, så jeg definerede følgende 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 } 

Denne funktion bestemmer først antallet af linjer i filen og bruger derefter sed til at udtrække linje efter linje og sender hver linje som et enkeltstrengargument til et givet fungere. Jeg formoder, at dette måske bliver virkelig ineffektivt med store filer, men det har ikke været et problem for mig hidtil (forslag til, hvordan man forbedrer denne velkomst selvfølgelig).

Brugen er ret sød 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 

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *