Stel dat ik het volgende Bash-script heb:
while read SCRIPT_SOURCE_LINE; do
echo "$SCRIPT_SOURCE_LINE"
done
Ik heb gemerkt dat voor bestanden zonder een nieuwe regel aan het einde, dit de laatste regel in feite overslaat.
Ik heb gezocht naar een oplossing en vond dit:
Als het lezen in plaats daarvan het einde van het bestand bereikt
van end-of-line, staat er wel in de
gegevens en wijs deze toe aan de variabelen,
maar het wordt afgesloten met een status die niet nul is.
Als uw lus is geconstrueerd “terwijl
lezen ;doe dingen ;gedaanDus in plaats van de read exit te testen
status direct, test een vlag en heb
het leescommando zet die vlag van
binnen het luslichaam. Op die manier
ongeacht leest de exit-status, de
hele loop body wordt uitgevoerd, omdat read
was slechts een van de opdrachten in de lijst
in de lus als alle andere, niet a
beslissende factor of de lus zal
helemaal rennen.DONE=false until $DONE ;do read || DONE=true # process $REPLY here done < /path/to/file.in
Hoe kan ik deze oplossing herschrijven zodat deze zich precies hetzelfde gedraagt als de while
-lus die ik eerder had, d.w.z. zonder de locatie van het invoerbestand hard te coderen?
Antwoord 1, autoriteit 100%
Ik gebruik de volgende constructie:
while IFS= read -r LINE || [[ -n "$LINE" ]]; do
echo "$LINE"
done
Het werkt met vrijwel alles behalve null-tekens in de invoer:
- Bestanden die beginnen of eindigen met lege regels
- Regels die beginnen of eindigen met witruimte
- Bestanden die geen eindigende nieuwe regel hebben
Antwoord 2, autoriteit 40%
In je eerste voorbeeld neem ik aan dat je leest uit stdin. Om hetzelfde te doen met het tweede codeblok, hoeft u alleen maar de omleiding te verwijderen en $REPLY te herhalen:
DONE=false
until $DONE ;do
read || DONE=true
echo $REPLY
done
Antwoord 3, autoriteit 12%
Gebruik grep
met while-lus:
while IFS= read -r line; do
echo "$line"
done < <(grep "" file)
Als u grep .
gebruikt in plaats van grep ""
, worden de lege regels overgeslagen.
Opmerking:
-
Als u
IFS=
gebruikt, blijft elke regelinspringing intact. -
Bestand zonder een nieuwe regel aan het einde is geen standaard Unix-tekstbestand.
Antwoord 4, autoriteit 10%
Probeer in plaats van lezenGNU Coreutilste gebruiken zoals tee, cat, enz.
van stdin
readvalue=$(tee)
echo $readvalue
uit bestand
readvalue=$(cat filename)
echo $readvalue
Antwoord 5, autoriteit 5%
Dit is het patroon dat ik heb gebruikt:
while read -r; do
echo "${REPLY}"
done
[[ ${REPLY} ]] && echo "${REPLY}"
Wat werkt omdat zelfs als de while
-lus eindigt als de “test” van de read
eindigt met een code die niet nul is, read
vult nog steeds de ingebouwde variabele $REPLY
(of welke variabelen je ook kiest om toe te wijzen met read
).
Antwoord 6, autoriteit 2%
Het basisprobleem hier is dat read
foutniveau 1 retourneert wanneer het EOF tegenkomt, zelfs als het de variabele nog steeds correct invoert.
Je kunt het foutniveau van read
dus meteen in je lus gebruiken, anders worden de laatste gegevens niet geparseerd. Maar je zou dit kunnen doen:
eof=
while [ -z "$eof" ]; do
read SCRIPT_SOURCE_LINE || eof=true ## detect eof, but have a last round
echo "$SCRIPT_SOURCE_LINE"
done
Als je een zeer solide manier wilt om je regels te ontleden, gebruik dan:
IFS='' read -r LINE
Onthoud dat:
- NUL-teken wordt genegeerd
- als je vasthoudt aan het gebruik van
echo
om het gedrag vancat
na te bootsen, moet je eenecho -n
forceren nadat EOF is gedetecteerd ( je kunt de voorwaarde gebruiken[ "$eof" == true ]
)
Antwoord 7
Het antwoord van @Netcoder is goed, deze optimalisatie elimineert valse blanco regels, en zorgt er ook voor dat de laatste regel geen nieuwe regel heeft, als dat de oorspronkelijke regel was.
DONE=false
NL=
until $DONE ;do
if ! read ; then DONE=true ; NL='-n ';fi
echo $NL$REPLY
done
Ik heb een variant hiervan gebruikt om 2 functies te maken om tekst door te lijnen met een ‘[‘ om grep tevreden te houden. (u kunt andere vertalingen toevoegen)
function grepfix(){
local x="[email protected]";
if [[ "$x" == '-' ]]; then
local DONE=false
local xx=
until $DONE ;do
if ! IFS= read ; then DONE=true ; xx="-n "; fi
echo ${xx}${REPLY//\[/\\\[}
done
else
echo "${x//\[/\\\[}"
fi
}
function grepunfix(){
local x="[email protected]";
if [[ "$x" == '-' ]]; then
local DONE=false
local xx=
until $DONE ;do
if ! IFS= read ; then DONE=true ; xx="-n "; fi
echo ${xx}${REPLY//\\\[/\[}
done
else
echo "${x//\\\[/\[}"
fi
}
(passen – aangezien $1 pipe mogelijk maakt, worden anders alleen argumenten vertaald)
Antwoord 8
voor het lezen van bestanden met of zonder een nieuwe regel aan het einde:
cat "somefile" | { cat ; echo ; } | while read line; do echo $line; done
Bron: Mijn open source-project https://sourceforge.net/ projects/command-output-to-html-table/