Saya memiliki file 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
Saya ingin mencetak semua baris antara qwe
dan ewq
dalam file terpisah.
Inilah yang saya miliki sejauh ini:
#!/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"
Anda perlu membuat beberapa perubahan pada skrip Anda (tanpa urutan tertentu):
IFS=
sebelum read
untuk menghindari penghapusan spasi di depan dan di belakang.$line
tidak berubah dimanapun, tidak perlu variabel readLine
.Dengan perubahan-perubahan tersebut, skrip menjadi:
#!/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"
Yang bisa diringkas dengan cara ini:
#!/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"
Itu akan mencetak baris awal dan akhir (inklusif).
Jika tidak perlu mencetaknya, tukar tes awal dan akhir:
#!/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"
Namun, akan lebih baik (jika Anda memiliki bash versi 4.0 atau lebih baik) untuk menggunakan readarray
dan loop dengan elemen array:
#!/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
Itu akan menghindari sebagian besar masalah menggunakan read
.
Tentu saja, anda bisa menggunakan baris sed
yang direkomendasikan (dalam komentar; Terima kasih, @costas) untuk mendapatkan hanya baris yang akan diproses:
#!/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
Seperti yang ditunjukkan oleh @Costas, alat yang tepat untuk digunakan untuk pekerjaan ini adalah sed
:
sed '/qwe/,/ewq/ w other.file' foo.txt
Mungkin ada pemrosesan lain yang diperlukan pada baris yang akan dicetak. Itu tidak masalah; lakukan saja seperti itu:
sed -e '/qwe/,/ewq/{w other.file' -e 'other processing;}' foo.txt
(Tentu saja, "pemrosesan lain &" bukanlah perintah sed
yang sebenarnya.) Di atas adalah pola yang digunakan jika anda perlu melakukan pemrosesan setelah anda mencetak baris. Jika anda ingin melakukan pemrosesan lain dan kemudian mencetak versi perubahan dari baris tersebut (yang sepertinya lebih mungkin), anda akan menggunakan:
sed -e '/qwe/,/ewq/{processing;w other.file' -e '}' foo.txt
(Perhatikan bahwa perlu untuk meletakkan }
ke dalam argumennya sendiri, jika tidak, maka akan ditafsirkan sebagai bagian dari nama other.file
).
Anda (OP) belum menyatakan apa "pemrosesan lain &" yang harus Anda lakukan pada baris-baris tersebut, atau saya bisa lebih spesifik. Tetapi apapun pemrosesan itu, anda pasti bisa melakukannya di sed
-atau jika itu terlalu berat, anda bisa melakukannya di awk
dengan sedikit perubahan pada kode di atas:
awk '/qwe/,/ewq/ { print > "other.file" }' foo.txt
Kemudian, Anda memiliki semua kekuatan bahasa pemrograman awk
yang dapat Anda gunakan untuk melakukan pemrosesan pada baris-baris sebelum Anda mengeksekusi pernyataan print
. Dan tentu saja awk
(dan sed
) adalah dirancang untuk pemrosesan teks, tidak seperti bash
.
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
Itu adalah salah satu cara yang sangat lambat untuk melakukannya. Dengan GNU grep
dan infile
biasa:
IFS=
while grep -xm1 qwe
do while read -r in &&
[ ewq != "$in" ]
do printf %s\\n "$in"
: some processing
done
done <infile
...setidaknya akan mengoptimalkan setengah dari pembacaan yang tidak efisien...
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'
Dan itu akan menghindari ketidakefisienan read
sama sekali untuk sebagian besar sh
's di luar sana, meskipun harus mencetak output dua kali - sekali dikutip ke sh
dan sekali tidak dikutip ke stdout. Ini bekerja secara berbeda karena perintah .
cenderung membaca input per blok daripada per byte untuk sebagian besar implementasi. Namun, ini tidak memerlukan ewq - qwe sama sekali, dan akan bekerja untuk input yang dialirkan - seperti 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