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. Katsowhile 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