Kuinka silmukata tiedoston viivojen yli?

Sano, että minulla on tämä tiedosto:

hello world hello world 

Tämä ohjelma

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

tuotokset

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

Haluan, että for iteroi jokaisella rivillä erikseen jättämättä kuitenkaan välilyöntejä, eli kaksi viimeistä riviä tulisi korvata

tester: hello world 

Lainausmerkkien avulla for i in "$(cat $1)"; johtaa siihen, että i nimetään koko tiedosto kerralla. Mitä minun pitäisi muuttaa?

Vastaa

(9 vuotta myöhemmin 🙂
Molemmat vastaukset epäonnistuvat tiedostoissa, joissa ei ole uutta riviä lopussa, tämä ohittaa tehokkaasti viimeisen rivin, ei aiheuta virheitä, johtaa katastrofiin (oppinut kovasti tapa :).

Paras tiivis ratkaisu, jonka löysin tähän mennessä, että ”Just Works” (sekä bashissa että sh: ssä):

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

Katso perusteellisempi keskustelu tästä StackOverflow-keskustelusta: Kuinka käyttää lue ”(Bash) lukeaksesi tiedoston viimeisen rivin, jos tiedoston lopussa ei ole uutta riviä?

Varo: tämä lähestymistapa lisää ylimääräisen uuden rivin viimeiseen riviin, jos sellaista on ei yhtään.

Kommentit

  • Hieno saalis, kiitos! Huomaa, että " Varo, että tämä lisää uuden rivin EOF: lle, jos sellaista ei vielä ole. " kommentti kuitenkin
  • Tobias, minä ' lisän tämän muistiinpanoksi, kiitos.

vastaus

Kanssa for ja IFS :

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

Huomaa kuitenkin, että se ohittaa tyhjät rivit uusi rivi IFS-välilyönninä, sekvensseinä se lasketaan yhdeksi ja etu- ja jälkimmäiset jätetään huomiotta. zsh ja ksh93 (ei bash) avulla voit muuttaa sen muotoon IFS=$"\n\n" jotta rivinvaihtoa ei käsitellä erikseen, huomaa kuitenkin, että kaikki lopussa olevat uudet rivimerkit (niin että myös tyhjät rivit loppuvat) poistetaan aina komennon korvaamalla.

Tai ja read (ei enempää cat ):

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

Siellä tyhjät rivit säilyvät, mutta huomaa, että se ohittaa viimeisen rivin, jos sitä ei ole erotettu kunnolla uudella rivillä.

kommentit

  • kiitos, en tiennyt ' tiennyt, että < kokonaiseksi silmukaksi. Vaikka onkin järkevää nyt, näin sen
  • Näen IFS \ read -r line' in second example. Is really IFS = `tarvitaan? IMHO riittää sanomaan: while read -r line; do echo "tester: $line"; done < "$1"
  • @GrzegorzWierzowiecki IFS= poistaa etu- ja lopputilan tyhjentämisen. Katso while IFS= read.. -osiossa, miksi IFS: llä ei ole vaikutusta?
  • @BenMares Estääksesi huijaamisen lausekkeessa mahdollisesti esiintyvät lausekkeet laajentamisesta vastaaviin tiedostojen nimiin. Kokeile esimerkiksi printf '%s\n' '*o' 'bar' >afile; touch foo; IFS=$'\n'; for i in $(cat afile); do echo "$i"; done.
  • A while IFS= read -r line || [ "$line" ]; do käsittelee takarivin, jota ei ole rajoitettu uudella rivillä. (mutta se lisätään takaisin).

Vastaa

Minun on tehtävä, mitä se kannattaa että melko usein, enkä voi koskaan muistaa tarkkaa tapaa käyttää while IFS= read..., joten määritin seuraavan funktion bash-profiiliini:

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

Tämä toiminto määrittää ensin tiedostossa olevien rivien määrän, käyttää sitten sed purkamaan rivin rivin jälkeen ja välittää jokaisen rivin yhtenä merkkijonona argumenttina mihin tahansa annettuun toiminto. Oletan, että tämä saattaa olla todella tehotonta suurilla tiedostoilla, mutta se ei ole ollut minulle toistaiseksi ongelma (tietysti ehdotuksia tämän tervetulon parantamiseksi).

Käyttö on melko suloista 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 

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *