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
qweと
ewq`の間のすべての行を別のファイルに出力したいのです。
今のところ、以下のようになっています。
#!/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"
スクリプトにいくつかの変更を加える必要があります(順不同)。
の前に
IFS=` を使用して、先頭と末尾のスペースを削除しないようにします。はどこにも変更されないので、変数
readLine` は必要ありません。以上の変更で、スクリプトは次のようになります。
#!/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"
これを要約すると、次のようになります。
#!/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"
これで、開始行と終了行(含む)が表示されます。
印刷する必要がない場合は、開始と終了のテストを入れ替えてください。
#!/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"
しかし、(bashのバージョンが4.0以上であれば)readarray
を使用して、配列の要素でループする方がはるかに良いでしょう。
#!/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
これにより、read
を使用した場合の問題点のほとんどを回避することができます。
もちろん、推奨されている(コメントにある。Thanks, @costas)sed
行を使って、処理対象の行だけを取得することもできます。
#!/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氏が指摘したように、この作業に使用する正しいツールは sed
です。
sed '/qwe/,/ewq/ w other.file' foo.txt
印刷される行には他の処理が必要かもしれません。 それはそれでいいのですが、そのようにしてください。
sed -e '/qwe/,/ewq/{w other.file' -e 'other processing;}' foo.txt
(もちろん、"other processing"は本当のsed
コマンドではありません。) 上記は、行を印刷した後に*処理を行う必要がある場合に使用するパターンです。 もし、他の処理をしてから、その行の変更したバージョンを印刷したい場合(より可能性が高いと思われる)は、次のように使います。
sed -e '/qwe/,/ewq/{processing;w other.file' -e '}' foo.txt
(ただし、}
を単独の引数にする必要があります。そうしないと、other.file
の名前の一部として解釈されてしまいます。)
あなた(OP)は、その行でどのような "other processing" をしなければならないのかを述べていないので、もう少し具体的に説明することができます。 しかし、その処理が何であれ、sed
で行うことは間違いありませんし、もしそれが扱いにくいものであれば、上記のコードにほとんど変更を加えることなくawk
で行うこともできます。
awk '/qwe/,/ewq/ { print > "other.file" }' foo.txt
そうすれば、awk
プログラミング言語のすべての力を自由に使うことができ、print
文を実行する前の行で処理を行うことができます。 もちろん awk
(および sed
) は 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
これは本当に遅い方法の一つです。GNU grep
と通常の infile
を使っています。
IFS=
while grep -xm1 qwe
do while read -r in &&
[ ewq != "$in" ]
do printf %s\\n "$in"
: some processing
done
done <infile
...少なくとも非効率的な読み込みの半分は最適化されるでしょう...
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'
これにより、ほとんどの sh
'において read
の非効率性を完全に回避することができます。ただし、出力を2回(1回は sh
に引用し、1回は標準出力に引用しない)印刷する必要があります。ほとんどの実装では、.
コマンドは入力をバイト単位ではなく、ブロック単位で読み込む傾向があるため、動作が異なります。しかし、EWQ-QWEを完全に排除することができ、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