Bir foo.txt dosyam var
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
qweve
ewq` arasındaki tüm satırları ayrı bir dosyada yazdırmak istiyorum.
Şimdiye kadar sahip olduğum şey bu:
#!/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"
Senaryonuzda bazı değişiklikler yapmanız gerekiyor (belirli bir sıraya göre değil):
read
den önce IFS=
kullanın.hiçbir yerde değiştirilmediği için
readLine` değişkenine gerek yoktur.Bu değişikliklerle senaryo şu hale geliyor:
#!/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"
Bu şekilde özetlenebilir:
#!/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"
Bu, başlangıç ve bitiş satırlarını (dahil) yazdıracaktır.
Bunları yazdırmaya gerek yoksa, başlangıç ve bitiş testlerini değiştirin:
#!/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"
Bununla birlikte, readarray
kullanmak ve dizi elemanları ile döngü yapmak oldukça daha iyi olacaktır (eğer bash sürüm 4.0 veya daha iyisine sahipseniz):
#!/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
Bu, read
kullanımındaki sorunların çoğunu önleyecektir.
Elbette, yalnızca işlenecek satırları almak için önerilen (yorumlarda; Teşekkürler, @costas) sed
satırını kullanabilirsiniz:
#!/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
Costas'ın da belirttiği gibi, bu iş için kullanılacak doğru araç sed
dir:
sed '/qwe/,/ewq/ w other.file' foo.txt
Yazdırılacak satırlar üzerinde başka işlemler yapılması gerekebilir. Sorun değil; sadece bu şekilde yapın:
sed -e '/qwe/,/ewq/{w other.file' -e 'other processing;}' foo.txt
(Tabii ki, "other processing" gerçek bir `sed' komutu değildir.) Yukarıdaki, işleminizi satırı yazdırdıktan sonra yapmanız gerekiyorsa kullanılacak kalıptır. Başka bir işlem yapmak ve ardından satırın değiştirilmiş bir sürümünü yazdırmak istiyorsanız (ki bu daha olası görünüyor), şunu kullanırsınız:
sed -e '/qwe/,/ewq/{processing;w other.file' -e '}' foo.txt
(}
argümanını kendi argümanına koymak gerektiğine dikkat edin, aksi takdirde other.file
adının bir parçası olarak yorumlanacaktır).
Siz (OP) satırlarda hangi "diğer işlemleri" yapmanız gerektiğini belirtmediniz, yoksa daha spesifik olabilirdim. Ancak bu işlem ne olursa olsun, bunu kesinlikle sed
içinde yapabilirsiniz veya bu çok hantal olursa, yukarıdaki kodda çok az değişiklik yaparak awk
içinde yapabilirsiniz:
awk '/qwe/,/ewq/ { print > "other.file" }' foo.txt
O zaman, print
deyimini çalıştırmadan önce satırlar üzerinde işlem yapmak için awk
programlama dilinin tüm gücüne sahip olursunuz. Ve tabii ki awk
(ve sed
) bash
ın aksine metin işleme için tasarlanmıştır.
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
Bu, bunu yapmanın gerçekten yavaş bir yoludur. Bir GNU grep
ve normal bir infile
ile:
IFS=
while grep -xm1 qwe
do while read -r in &&
[ ewq != "$in" ]
do printf %s\\n "$in"
: some processing
done
done <infile
...en azından verimsiz okumaların yarısını optimize ederdi...
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'
Ve bu, read
komutunun verimsizliğini çoğu sh
's için tamamen önleyecektir, ancak çıktıyı iki kez yazdırmak zorunda kalacaktır - bir kez sh
'a alıntılanmış ve bir kez de stdout'a alıntılanmamış. Farklı çalışır çünkü .
komutu çoğu uygulamada girdiyi bayt yerine blok olarak okuma eğilimindedir. Yine de, ewq - qwe'yi tamamen ortadan kaldırır ve bir FIFO gibi akışlı girdi için işe yarar.
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