Hvordan løpe over linjene i en fil?

Si at jeg har denne filen:

hello world hello world 

Dette programmet

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

utganger

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

Jeg vil gjerne ha for gjentas over hver linje individuelt og ignorerer imidlertid mellomrommene, dvs. de to siste linjene bør erstattes av

tester: hello world 

Bruk av anførselstegn for i in "$(cat $1)"; resulterer i at i tildeles hele filen på en gang. Hva skal jeg endre?

Svar

(9 år senere 🙂
Begge gitt svarene mislyktes på filer uten en ny linje på slutten, dette vil effektivt hoppe over den siste linjen, gi ingen feil, vil føre til katastrofe (lærte hardt måte :).

Den beste konsise løsningen jeg har funnet så langt 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 

For mer grundig diskusjon, se denne StackOverflow-diskusjonen: Hvordan bruke «mens les «(Bash) for å lese den siste linjen i en fil hvis det ikke er noen ny linje på slutten av filen?

Vær forsiktig: denne tilnærmingen legger til en ekstra ny linje til den siste linjen hvis det er ingen allerede.

Kommentarer

  • Fin fangst, takk! Legg merke til " Vær oppmerksom på at dette legger til en ekstra ny linje på EOF hvis det ikke allerede er noen. " kommentar skjønt
  • Tobias, jeg ' Jeg legger til dette som et notat, takk.

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 

Merk imidlertid at det vil hoppe over tomme linjer som ny linje som et IFS-hvitt mellomromstegn, sekvenser av det teller som 1 og de ledende og etterfølgende blir ignorert. Med zsh og ksh93 (ikke bash) kan du endre det til IFS=$"\n\n" for ny linje som ikke skal behandles spesielt, men vær oppmerksom på at alle etterfølgende nye linjetegn (slik at de inkluderer etterfølgende tomme linjer) alltid vil bli fjernet med kommandosubstitusjonen.

Eller med read (ikke mer cat ):

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

Der er tomme linjer bevart, men merk at det vil hoppe over den siste linjen hvis den ikke ble avgrenset ordentlig av et nytt linjetegn.

Kommentarer

  • takk, jeg visste ikke ' jeg kunne < i en hel løkke. Selv om det er helt fornuftig nå så jeg det
  • Jeg ser IFS \ read -r line' in second example. Is really IFS = `nødvendig? IMHO er det nok å si: while read -r line; do echo "tester: $line"; done < "$1"
  • @GrzegorzWierzowiecki IFS= slår av stripping av ledende og etterfølgende mellomrom. Se I while IFS= read.., hvorfor har IFS ingen effekt?
  • @BenMares For å forhindre globbing uttrykk som muligens vises i teksten vi leser fra utvides til samsvarende filnavn. Prøv for eksempel 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 vil behandle en etterfølgende linje som ikke er riktig avgrenset av et nytt linjetegn (men det vil bli lagt tilbake).

Svar

For hva det er verdt, må jeg gjøre det ganske ofte, og kan aldri huske den eksakte måten å bruke while IFS= read..., så jeg definerte følgende funksjon 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 funksjonen bestemmer først antall linjer i filen, bruker deretter sed for å trekke ut linje etter linje, og sender hver linje som et enkeltstrengargument til et gitt funksjon. Jeg antar at dette kan bli veldig ineffektivt med store filer, men det har ikke vært et problem for meg så langt (forslag til hvordan du kan forbedre denne velkomst selvfølgelig).

Bruken er ganske 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 

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *