ファイルを1行ずつ読み、条件が満たされたら、次の条件が出るまで読み続けます。 [閉じる]

ファイルを1行ずつ読み、条件が満たされたら、次の条件が出るまで読み続けます。 [閉じる]

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"

答え1

特定の順序なしにスクリプトをいくつか変更する必要があります。

  • 先行スペースと末尾のスペースを削除したくない場合は、IFS=beforeを使用してください。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


もちろんsed、処理したい行のみを取得するために推奨(コメント、ありがとう、@costas)行を使用できます。

    #!/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 

答え2

@Costasが指摘したように、作業に適したツールは次のとおりですsed

sed '/qwe/,/ewq/ w other.file' foo.txt

印刷する行の追加処理が必要な場合があります。大丈夫です。こうしてください。

sed -e '/qwe/,/ewq/{w other.file' -e 'other processing;}' foo.txt

(もちろん、「他の処理」は実際のsedコマンドではありません。)上記は、処理が必要な場合に使用するパターンです。後ろにラインを印刷します。追加の処理を実行してから変更されたバージョンの行を印刷するには(可能性が高い)、次のように使用できます。

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

答え3

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

これは非常に遅い方法です。 GNUgrepと一般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'

readこれにより、出力を2回印刷する必要がありますが、ほとんどshの場合、非効率性を回避できます。一度はstdoutへの参照を使用しsh、一度はstdoutへの参照なしで印刷します。.ほとんどの実装では、コマンドはバイトではなくチャンクで入力を読み取ることを好むため、動作が異なります。それにもかかわらず、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

答え4

sed '/./=' input2.file | sed -n '/./N;s/\n/  /; /qwe/,/ewq/p'

関連情報