web-dev-qa-db-ja.com

sedが完全な行を待たないようにする方法はありますか?

sedは入力を1行ずつ処理しているようです。これは通常問題ではありませんが、非常に長い行がある場合は、問題が発生する可能性があります。たとえば、次のコマンドは何も出力しません。

{ while :; do echo -n a; done } | sed "s/a/b/"

素朴なことに、文字を受信するとすぐに何かを出力できるはずです(少なくともこのパターンでは)。私はもう試した sed -u、しかしそれは何も変更しません。

1
Peltier

sed FAQ から:

Sedは次のように機能します。sedは一度に1行を読み取り、終了する改行を切り取り、残ったものをsedスクリプトがアドレス指定または変更できるパターンスペースに配置します...

したがって、sedは、入力に改行が発生する前にデータを実際に確認しません。

あなたの単純なケースでは、単純なtr 'a' 'b'sedを効率的に置き換えます

それ以外の場合は、入力にLFを追加し、sed出力でそれらを削除します。

3
xenoid

sedの特別な実装である必要がありました。私はそのような実装が存在することを期待していません。

理由:

  • sedのPOSIX仕様 は、「入力ファイルはテキストファイルでなければならない」と明示的に述べています。 テキストファイル 「NUL文字を含まず、改行文字を含め、長さが{LINE_MAX}バイトを超えることはできない」行で構成されます。 {LINE_MAX}{_POSIX2_LINE_MAX}以上であり、後者は2048link )です。したがって、非常に長い行の問題は「正常」です( this と比較してください)。
  • sedはパターンを取りません、スクリプトを取ります。スクリプトを実行する前にパターンスペースに1行を読み取り、実行してから(変更された)パターンスペースを出力し、次の行を読み取ります。

    あなたの場合、それは「少なくともこのパターンで可能であるはずです」ではなく、「少なくともこのスクリプトで」です。ツールはスクリプト全体を分析し、行ベースの動作を変更する必要があるかどうかを判断する必要があります。いくつかの単純なスクリプト(あなたのような)は理論的にこれを可能にするかもしれません。あなたのようなパターンはうまくいくかもしれませんが、少し複雑なパターンでも問題が発生します。例:

    s/a.*b/c/では、.*スニペットは貪欲です。入力ストリームの長さは制限されていません。最後のbを見つける方法は?

    私のポイントは、動作の変更は、いくつかの単純なスクリプト、いくつかの単純なパターンに対して安全に実行できるということです。より複雑なスクリプト/パターンが理論的に適格であり、この事実を利用したい場合は、分析ロジックをより複雑にする必要があります。また、入力がPOSIXに関してテキストであることが判明した場合(sedはこれを事前に知らないことに注意してください)、この「最適化」は重要ではありません。

    sedが必要なものをサポートしている場合、毎回追加の作業を行う必要があります。また、代替動作を担当する追加のコードを維持する必要があります。いくつかのまれなニーズをカバーするためにこれすべて。解決策は、オプトイン方式でこの追加機能を許可するオプションです。


少なくともスクリプトが十分に単純な場合は、必要なことを実行できるツールがあります。 bbe (Debianのbbeパッケージ)です。 sedでの検索と置換がグローバル(s/a/b/g)の場合、次のように進めることができます。

{ while :; do echo -n a; done } | bbe -e 's/a/b/'
# output may not appear immediately, but it will

私のテストでは、このツールは常にs/foo/bar/をグローバルとして扱うことが示されています。

これを回避するには、処理するデータのブロックを指定する必要があります(man 1 bbeを参照)。次のバリアントは、入力ストリームの先頭からaの最初の出現までブロックを処理します(残りのバイトは変更されずに通過します。そのようなブロックが見つからない場合、ストリーム全体が変更されずに通過します):

bbe -b '0:/a/' -e 's/a/b/'

このようにして、全体ストリームで最大1つのabに置き換えられます。

各行の最初のbbeのみを置き換えるためにaが必要な場合、つまりsedのs/a/b/をエミュレートする必要がある場合、おそらく非常に長い行で、または最後の行が終了しない場合は注意が必要です。トリックは、改行文字で始まり、aで終わるブロックを定義することです。ブロック内にないものは変更されません。このコマンド:

bbe -b '/\n/:/a/' -e 's/a/b/'

各行の最初のaのみを置き換えますが、最初の行を置き換えます(最初の行の前に改行文字がないため、ブロックに属することはできません)。これに対処するには、後で削除する追加の改行を追加する必要があります。

最後の行が「neverending」の複数行入力の処理例:

{ echo     # extra newline, it will be removed by tail later
  echo "Actual input starts with capital A."
  echo "Note there are multiple 'a' characters in this line."
  # "neverending" line generator below
  while :; do echo -n a; done
} | bbe -b '/\n/:/a/' -e 's/a/b/' | tail -c +2
# output may not appear immediately, but it will

出力:

Actubl input starts with capital A.
Note there bre multiple 'a' characters in this line.
baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa…

ノート:

  • bbe正規表現のサポートは存在しないようです(?)。
  • bbeはバイトで機能し、sedは文字(一般にマルチバイト文字)で機能します。
3