Stel dat ik dit bestand heb:
hello world hello world
Dit programma
#!/bin/bash for i in $(cat $1); do echo "tester: $i" done
outputs
tester: hello tester: world tester: hello tester: world
Ik “zou graag de for
herhaal over elke regel afzonderlijk en negeer echter witruimten, dwz de laatste twee regels moeten worden vervangen door
tester: hello world
Aanhalingstekens gebruiken for i in "$(cat $1)";
resulteert erin dat i
het hele bestand in één keer wordt toegewezen. Wat moet ik wijzigen?
Antwoord
(9 jaar later 🙂
Beide verstrekte antwoorden zouden mislukken op bestanden zonder een nieuwe regel aan het einde, dit zal effectief de laatste regel overslaan, geen fouten produceren, zou tot een ramp leiden (hard geleerd way :).
De beste beknopte oplossing die ik tot nu toe heb gevonden is dat “Just Works” (in zowel bash als sh):
while IFS="" read -r LINE || [ -n "${LINE}" ]; do echo "processing line: ${LINE}" done < /path/to/input/file.txt
Zie deze StackOverflow-discussie voor een meer diepgaande discussie: Hoe “while read “(Bash) om de laatste regel in een bestand te lezen als er geen nieuwe regel aan het einde van het bestand staat?
Let op: deze benadering voegt een extra nieuwe regel toe aan de laatste regel als die er is geen al.
Reacties
- Leuke vangst, bedankt! Let op de " Pas op dat dit een extra nieuwe regel toevoegt aan EOF als die er al is. " opmerking echter
- Tobias, ik ' zal dit als een opmerking toevoegen, bedankt.
Antwoord
Met for
en 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 echter op dat het lege regels zal overslaan omdat nieuwe regel een IFS-witruimte-teken is, reeksen daarvan tellen als 1 en de voorste en achterste worden genegeerd. Met zsh
en ksh93
(niet bash
) kun je deze wijzigen in IFS=$"\n\n"
om nieuwe regel niet speciaal te behandelen, merk er echter op dat alle achterliggende tekens voor nieuwe regels (dus inclusief lege regels aan het einde) altijd zullen worden verwijderd door de opdrachtsubstitutie.
Of met read
(niet meer cat
):
#!/bin/bash while IFS= read -r line; do echo "tester: $line" done < "$1"
Daar blijven lege regels behouden, maar merk op dat het de laatste regel zou overslaan als deze niet correct werd gescheiden door een nieuw-regel-teken.
Reacties
- bedankt, ik wist niet ' niet dat iemand
<
in een hele lus. Hoewel het nu volkomen logisch is, zag ik het - Ik zie
IFS \ read -r line' in second example. Is really
IFS = `nodig? IMHO het genoeg om te zeggen:while read -r line; do echo "tester: $line"; done < "$1"
- @GrzegorzWierzowiecki
IFS=
schakelt het verwijderen van voorloop- en volgspaties uit. Zie Inwhile IFS= read..
, waarom heeft IFS geen effect? - @BenMares Om globbing te voorkomen uitdrukkingen die mogelijk voorkomen in de tekst die we lezen, worden uitgebreid naar overeenkomende bestandsnamen. Probeer bijvoorbeeld
printf '%s\n' '*o' 'bar' >afile; touch foo; IFS=$'\n'; for i in $(cat afile); do echo "$i"; done
. - Een
while IFS= read -r line || [ "$line" ]; do
verwerkt een regel aan het einde die niet correct wordt gescheiden door een teken voor een nieuwe regel (maar het wordt weer toegevoegd).
Antwoord
Voor wat het waard is, moet ik doen dat vrij vaak, en ik kan me nooit de exacte manier herinneren om while IFS= read...
te gebruiken, dus definieerde ik de volgende functie in mijn bash-profiel:
# 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 }
Deze functie bepaalt eerst het aantal regels in het bestand, gebruikt vervolgens sed
om regel na regel te extraheren, en geeft elke regel als een enkel stringargument door aan een gegeven functie. Ik veronderstel dat dit erg inefficiënt kan worden met grote bestanden, maar dat is tot dusver geen probleem voor mij geweest (suggesties om dit welkom te verbeteren natuurlijk).
Het gebruik is best aardig 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