ファイル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=
の前にread
を付けると、先頭と末尾のスペースが削除されなくなります。$line
はどこでも変更されません。変数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"
ただし、readarray
を使用して配列要素でループするのは(bashバージョン4.0以降の場合)非常に優れています。
#!/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
の使用に関する問題のほとんどが回避されます。
もちろん、推奨される(コメント;ありがとう、@ 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
(もちろん、「その他の処理」は実際のsed
コマンドではありません。)上記は、処理を行う必要がある場合に使用するパターンですafter行を印刷します。他の処理を行い、変更されたバージョンの行を出力する場合(これは可能性が高いと思われます)、以下を使用します。
sed -e '/qwe/,/ewq/{processing;w other.file' -e '}' foo.txt
(}
を独自の引数に入れる必要があることに注意してください。そうしないと、other.file
名の一部として解釈されます。)
あなた(OP)は、あなたがライン上でしなければならない「その他の処理」を述べていません、あるいは私はより具体的かもしれません。しかし、その処理が何であれ、sed
で確実に実行できます。または、扱いにくい場合は、上記のコードをほとんど変更せずにawk
で実行できます。
awk '/qwe/,/ewq/ { print > "other.file" }' foo.txt
そうすれば、awk
ステートメントを実行する前に、print
プログラミング言語のすべての機能を利用して、行を処理することができます。そしてもちろん、awk
とは異なり、sed
(およびbash
)は設計済みであり、テキスト処理用です。