Ich habe eine Datei foo.txt
test
qwe
asd
xca
asdfarrf
sxcad
asdfa
sdca
dac
dacqa
ea
sdcv
asgfa
sdcv
ewq
qwe
a
df
fa
vas
fg
fasdf
eqw
qwe
aefawasd
adfae
asdfwe
asdf
era
fbn
tsgnjd
nuydid
hyhnydf
gby
asfga
dsg
eqw
qwe
rtargt
raga
adfgasgaa
asgarhsdtj
shyjuysy
sdgh
jstht
ewq
sdtjstsa
sdghysdmks
aadfbgns,
asfhytewat
bafg
q4t
qwe
asfdg5ab
fgshtsadtyh
wafbvg
nasfga
ghafg
ewq
qwe
afghta
asg56ang
adfg643
5aasdfgr5
asdfg
fdagh5t
ewq
Ich möchte alle Zeilen zwischen qwe
und ewq
in einer separaten Datei ausgeben.
Das ist, was ich bis jetzt habe:
#!/bin/bash
filename="foo.txt"
#While loop to read line by line
while read -r line
do
readLine=$line
#If the line starts with ST then echo the line
if [[ $readLine = qwe* ]] ; then
echo "$readLine"
read line
readLine=$line
if [[ $readLine = ewq* ]] ; then
echo "$readLine"
fi
fi
done < "$filename"
Sie müssen einige Änderungen an Ihrem Skript vornehmen (in keiner bestimmten Reihenfolge):
IFS=
vor read
, um das Entfernen führender und nachfolgender Leerzeichen zu vermeiden.$line
nirgends geändert wird, ist die Variable readLine
nicht erforderlich.read
nicht in der Mitte der Schleife!!!Mit diesen Änderungen wird das Skript zu:
#!/bin/bash
filename="foo.txt"
#While loop to read line by line
while IFS= read -r line; do
#If the line starts with ST then set var to yes.
if [[ $line == qwe* ]] ; then
printline="yes"
# Just t make each line start very clear, remove in use.
echo "----------------------->>"
fi
# If variable is yes, print the line.
if [[ $printline == "yes" ]] ; then
echo "$line"
fi
#If the line starts with ST then set var to no.
if [[ $line == ewq* ]] ; then
printline="no"
# Just to make each line end very clear, remove in use.
echo "----------------------------<<"
fi
done < "$filename"
Das könnte man folgendermaßen zusammenfassen:
#!/bin/bash
filename="foo.txt"
while IFS= read -r line; do
[[ $line == qwe* ]] && printline="yes"
[[ $printline == "yes" ]] && echo "$line"
[[ $line == ewq* ]] && printline="no"
done < "$filename"
Damit werden die Anfangs- und Endzeilen (einschließlich) gedruckt.
Wenn keine Notwendigkeit besteht, sie zu drucken, vertauschen Sie die Start- und Endtests:
#!/bin/bash
filename="foo.txt"
while IFS= read -r line; do
[[ $line == ewq* ]] && printline="no"
[[ $printline == "yes" ]] && echo "$line"
[[ $line == qwe* ]] && printline="yes"
done < "$filename"
Es wäre jedoch besser (wenn Sie Bash-Version 4.0 oder besser haben), readarray
zu benutzen und eine Schleife mit den Array-Elementen zu machen:
#!/bin/dash
filename="infile"
readarray -t lines < "$filename"
for line in "${lines[@]}"; do
[[ $line == ewq* ]] && printline="no"
[[ $printline == "yes" ]] && echo "$line"
[[ $line == qwe* ]] && printline="yes"
done
Das wird die meisten Probleme der Verwendung von read
vermeiden.
Natuerlich koennte man auch die (in den Kommentaren empfohlene; Danke, @costas) sed
Zeile verwenden, um nur die zu verarbeitenden Zeilen zu erhalten:
#!/bin/bash
filename="foo.txt"
readarray -t lines <<< "$(sed -n '/^qwe.*/,/^ewq.*/p' "$filename")"
for line in "${lines[@]}"; do
: # Do all your additional processing here, with a clean input.
done
Wie @Costas schon sagte, ist das richtige Werkzeug für diese Aufgabe sed
:
sed '/qwe/,/ewq/ w other.file' foo.txt
Es kann sein, dass die zu druckenden Zeilen anders verarbeitet werden müssen. Das ist in Ordnung; machen Sie es einfach so:
sed -e '/qwe/,/ewq/{w other.file' -e 'other processing;}' foo.txt
(Natürlich ist "andere Verarbeitung" kein echter sed
-Befehl.) Das obige Muster ist zu verwenden, wenn Sie Ihre Verarbeitung nach dem Drucken der Zeile durchführen müssen. Wenn Sie eine andere Verarbeitung vornehmen und dann eine geänderte Version der Zeile ausdrucken wollen (was wahrscheinlicher ist), würden Sie es verwenden:
sed -e '/qwe/,/ewq/{processing;w other.file' -e '}' foo.txt
(Beachten Sie, dass es notwendig ist, das }
in ein eigenes Argument zu setzen, da es sonst als Teil des anderen.Dateinamens
interpretiert wird).
Sie (der OP) haben nicht angegeben, welche "andere Verarbeitung" Sie in den Zeilen vornehmen müssen, oder ich könnte genauer sein. Aber was auch immer diese Verarbeitung ist, man kann sie auf jeden Fall in sed
machen - oder wenn das zu unhandlich wird, kann man es in awk
machen, mit nur sehr geringen Änderungen am obigen Code:
awk '/qwe/,/ewq/ { print > "other.file" }' foo.txt
Dann haben Sie die ganze Macht der Programmiersprache awk
zur Verfügung, um die Zeilen zu verarbeiten, bevor Sie die Anweisung print
ausführen. Und natürlich sind awk
(und sed
) im Gegensatz zu bash
für die Textverarbeitung geplant.
qwe(){ printf %s\\n "$1"; }
ewq(){ :; }
IFS= ### prep the loop, only IFS= once
while read -r in
do case $in in
(qwe|ewq)
set "$in"
;;
("$processing"?)
"$process"
esac
"$1" "$in"
done
Das ist ein wirklich langsamer Weg, es zu tun. Mit einem GNU grep
und einem regulären infile
:
IFS=
while grep -xm1 qwe
do while read -r in &&
[ ewq != "$in" ]
do printf %s\\n "$in"
: some processing
done
done <infile
...würde zumindest die Hälfte der ineffizienten Lesevorgänge optimieren...
sed -ne '/^qwe$/,/^ewq$/H;$!{/^qwe$/!d;}' \
-e "x;s/'"'/&\\&&/g;s/\n/'"' '/g" \
-e "s/\(.*\) .e.*/p '\1/p" <input |
sh -c 'p(){ printf %s\\n "$@"
for l do : process "$l"
done
}; . /dev/fd/0'
Und das wuerde die Ineffizienzen von read
fuer die meisten sh
's da draussen ganz vermeiden, obwohl es die Ausgabe zweimal ausgeben muss - einmal zitiert nach sh
und einmal unquotiert nach stdout. Es funktioniert anders, weil der `.-Befehl bei den meisten Implementierungen dazu neigt, Eingaben blockweise und nicht byteweise zu lesen. Dennoch umgeht es ewq - qwe komplett und würde für gestreamte Eingaben funktionieren - wie z.B. ein FIFO.
qwe
asd
xca
asdfarrf
sxcad
asdfa
sdca
dac
dacqa
ea
sdcv
asgfa
sdcv
qwe
a
df
fa
vas
fg
fasdf
qwe
aefawasd
adfae
asdfwe
asdf
era
fbn
tsgnjd
nuydid
hyhnydf
gby
asfga
dsg
qwe
rtargt
raga
adfgasgaa
asgarhsdtj
shyjuysy
sdgh
jstht
qwe
asfdg5ab
fgshtsadtyh
wafbvg
nasfga
ghafg
qwe
afghta
asg56ang
adfg643
5aasdfgr5
asdfg
fdagh5t