パターンの最初の発生と最後の発生の間のすべての行をどのように取得できますか?

パターンの最初の発生と最後の発生の間のすべての行をどのように取得できますか?

fooパターンの最初の発生からパターンの最後の発生までの行だけを取得するようにファイル(入力ストリーム)をどのように切り捨てますかbar

たとえば、次の入力を考えてみます。

A line
like
foo
this 
foo
bar
something
something else
foo
bar
and
the
rest

次のような結果を期待しています。

foo
this 
foo
bar
something
something else
foo
bar

答え1

sed -n '/foo/{:a;N;/^\n/s/^\n//;/bar/{p;s/.*//;};ba};'

sed パターンマッチングは/first/,/second/1 行ずつ読みます。一部の行が一致する場合は、/first/それを覚えて、/second/そのパターンに最初に一致するものを探します。同時に、このモードで指定されたすべてのアクティビティが適用されます。その後、ファイルが終了するまでプロセスは引き続き開始されます。

それは私たちに必要なものではありません。最後に一致するパターンを見つける必要があります/second/。だから私たちは最初の入り口だけが見える建物を建てます/foo/。見つかるとループがa始まります。一致バッファに新しい行を追加し、Nパターンと一致することを確認します/bar/。もしそうなら、私たちはそれを印刷してマッチバッファを消去してからjanywayを使ってループの先頭にジャンプしますba

また、clean bufferを使用した後は、改行を削除する必要があります/^\n/s/^\n//。より良い解決策があると確信しています。残念ながら、私はそれを考えていませんでした。

すべてが明確になることを願っています。

答え2

私は少しPerlを使ってみましょう。

cat <<EOF | perl -ne 'BEGIN { $/ = undef; } print $1 if(/(foo.*bar)/s)'
A line
like
foo
this 
foo
bar
something
something else
foo
bar
and
the
rest
EOF

生産する

foo
this 
foo
bar
something
something else
foo
bar

答え3

以下は、大量のメモリを必要としない2段階のGNU sedソリューションです。

< infile                                     \
| sed -n '/foo/ { =; :a; z; N; /bar/=; ba }' \
| sed -n '1p; $p'                            \
| tr '\n' ' '                                \
| sed 's/ /,/; s/ /p/'                       \
| sed -n -f - infile

説明する

  • 最初の呼び出しはinfileを渡し、最初の発生とその後のすべての発生をsed見つけます。foobar
  • sedsedその後、これらのアドレスは2回の呼び出しと1回の呼び出しで構成される新しいスクリプトで構成されますtr。 3番目の出力は括弧なしsedです[start_address],[end_address]p
  • 最後に、呼び出しが再度sed転送され、infile見つかったアドレスとその間のすべての内容が印刷されます。

答え4

別のアプローチは次のとおりですsed

sed '/foo/,$!d;H;/bar/!d;s/.*//;x;s/\n//' infile

/foo/,$範囲内のすべての行(!この範囲にない行は削除されますd)をH古いスペースに追加します。bar次に、一致しない行を削除します。一致する行では、パターンスペースは空になり、eはx予約済みスペースに変わり、パターンスペースの前の空白行は削除されます。

入力量が多く、barこれがほとんど発生しない場合は、各行をパターンスペースにドラッグしてから毎回パターンスペースを確認するよりもはるかに高速ですbar
説明する:

sed '/foo/,$!d                     # delete line if not in this range
H                                  # append to hold space
/bar/!d                            # if it doesn't match bar, delete 
s/.*//                             # otherwise empty pattern space and
x                                  # exchange hold buffer w. pattern space then
s/\n//                             # remove the leading newline
' infile

もちろん、これがファイルでメモリに収まる場合は、次のように簡単に実行できます。

 ed -s infile<<'IN'
.t.
/foo/,?bar?p
q
IN

なぜならed できる今後検索そして後ろ。
シェルがプロセス置換をサポートしている場合は、コマンド出力をテキストバッファに読み込むこともできます。

printf '%s\n' .t. /foo/,?bar?p q | ed -s <(your command)

そうでない場合は、次のようにしますgnu ed

printf '%s\n' .t. /foo/,?bar?p q | ed -s '!your command'

関連情報