sed:1行入力時に失敗せずにファイル全体をパターンスペースに読み込みます。

sed:1行入力時に失敗せずにファイル全体をパターンスペースに読み込みます。

ファイル全体をパターン空間に読み込むことは、改行などを変更するのに役立ちます。そして、以下を提案する多くの例があります。

sed ':a;N;$!ba; [commands...]'

ただし、入力に1行しか含まれていない場合は失敗します。

たとえば、2行の入力がある場合、各行は代替コマンドの影響を受けます。

$ echo $'abc\ncat' | sed ':a;N;$!ba; s/a/xxx/g'
xxxbc
cxxxt

ただし、シングルライン入力を使用する場合、いいえ交換を行います。

$ echo 'abc' | sed ':a;N;$!ba; s/a/xxx/g'
abc

sedこの問題なく、すべての入力を一度に読み取るコマンドをどのように書くことができますか?

ベストアンサー1

ファイル全体をパターン空間に読み込むにはいくつかの理由があります。最後の行を囲む質問の論理的な問題は一般的です。これは行サイクルに関連しています。sed行がなくなり、EOF が発生すると終了sedし、処理は終了します。したがって、最後の行にあり、sed別の行をインポートするように指示すると、そこで停止し、もう何もしません。

つまり、ファイル全体をパターン空間に読み込む必要がある場合は、とにかく他のツールを検討する価値があります。実はsed同じ名前です小川エディタ - 一度に1行または1つの論理データブロックを操作するように設計されています。

全体のファイルブロックをよりよく処理する同様のツールがたくさんあります。たとえば、edandはほとんどの作業を実行できますが、それ以外にははるかに多くの作業を実行できますが、出力に変換中に入力ストリームで作業するだけでなく、ファイルシステムでも作業を実行します。一時バックアップファイルを保持します。その操作は必要に応じてディスクにバッファリングされ、ファイルの最後で突然終了しません。exsedsed(バッファ圧力下でははるかに頻繁に破裂しません)。またsed、ライン表示、元に戻す、名前付きバッファ、結合など、ストリーミングコンテキストでは理解できない多くの便利な機能も提供します。

sed最大の利点は、データを読み取った後すぐに迅速かつ効率的かつ継続的に処理できることです。ファイルを飲み込むと捨てます。そして上記の最後の行の問題だけでなく、バ​​ッファオーバーフローやパフォーマンスの低下など、極端な場合に問題が発生する傾向があります。解析するデータの長さが長くなるにつれて、一致を列挙すると正規表現エンジンの処理時間が長くなります。指数関数的に

最後のポイントに関して、注:例s/a/A/gは単なる例であり、入力から収集したい実際のスクリプトではないかもしれませんが、慣れるのに時間を費やす価値があることがわかりますy///gある文字をグローバルに別の文字に頻繁に置き換える場合、これはy非常に便利です。これは代替ではなく変換であり、正規表現を暗示しないため、はるかに高速です。後者は、空のアドレスを維持し、繰り返す場合にも便利です。//なぜなら、空のアドレスには影響しませんが、影響を受ける可能性があるからです。とにかくy/a/A/これは同じことを行うより簡単な方法です。たとえば、1y/aA/Aa/行のすべての大文字/小文字を置き換えるなどの置き換えも可能です。

また、あなたが説明する行動は何らかの形で起こるべき行動ではないことに注意してください。

info sedGNUから一般的なバグレポート部分:

  • Nコマンドは最後の行にあります

    • sedほとんどのExitバージョンは、コマンドがファイルの最後の行で実行されたときに何も印刷しません。もちろん、コマンドスイッチが指定されていない限り、NGNUはsed終了前にモードスペースを印刷します。-nこの選択は意図的に設計されています。

    • たとえば、の動作は、sed N foo barfooの行数が偶数か奇数かによって異なります。または、パターンマッチング後に次の数行を読むスクリプトを書くとき、伝統的な実装sedでは。/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }/foo/{ N;N;N;N;N;N;N;N;N; }

    • とにかく最も簡単な回避策は、$d;Nレガシー動作に依存するスクリプトでそれを使用するか、POSIXLY_CORRECT変数をnull以外の値に設定することです。

環境POSIXLY_CORRECT変数が言及されている理由は、POSIXがsed試行中にEOFに遭遇したときにN出力なしで終了する必要があることを示していますが、この場合、GNUバージョンは意図的に標準に違反するためです。また、上記の動作が合理的であっても、エラー条件がストリーム編集の1つであると仮定すると、ファイル全体がメモリに保存されません。

これ基準定義されたN動作:

  • N

    • 挿入された目線を使用して、追加されたデータを元のデータから分離し、次の入力行(終了行の縮小を除く\n)をパターンスペースに追加します。\n現在の行番号が変更されます。

    • 次の入力行が利用できない場合、Nコマンド動詞はスクリプトの最後に分岐して新しいループを開始するか、パターンスペースを標準出力にコピーせずに終了する必要があります。

:この時点で、質問に示されているいくつかの異なるGNU-isms、特にタグ、b牧草地、および{機能コンテキスト括弧の使用があります}。経験的に、任意の引数を許可するすべてのコマンドは、スクリプトの次の行で区切られていることがsedわかります。\nだから注文は...

:arbitrary_label_name; ...
b to_arbitrary_label_name; ...
//{ do arbitrary list of commands } ...

sed... 読み込む実装によっては、すべて異常に実行される可能性が高いです。移植可能な場合は、次のように書く必要があります。

...;:arbitrary_label_name
...;b to_arbitrary_label_name
//{ do arbitrary list of commands
}

r、、、、、、wtも同様に適用されますaic (多分今は忘れてしまった内容がもっとあるだろう)。ほとんどの場合、次のように書くこともできます。

sed -e :arbitrary_label_name -e b\ to_arbitary_label_name -e \
    "//{ do arbitrary list of commands" -e \}

...ここで、新しい-e実行ステートメントは\n行区切り文字を表します。したがって、GNUinfoテキストでは次のことをお勧めします。伝統的なsed実装では、次のことが必要です。:

/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N }

...実際にそうする必要があります...

/foo/{ $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N; $!N
}

...もちろんこれも事実ではない。このようにスクリプトを書くのは少し愚かです。次のように同じ目的を達成するためのより簡単な方法があります。

printf %s\\n foo . . . . . . |
sed -ne 'H;/foo/h;x;//s/\n/&/3p;tnd
         //!g;x;$!d;:nd' -e 'l;$a\' \
     -e 'this is the last line' 

...印刷:

foo
.
.
.
foo\n.\n.\n.$
.$
this is the last line

... ほとんどの命令と同様に、test命令は戻りsedレジスタをフラッシュするラインサイクルに依存し、ラインサイクルがここでほとんどの操作を実行できるようになるためです。これは、ファイルを読み取るときに発生するもう1つのトレードオフです。行サイクルは再フラッシュされないため、多くのテストが異常に機能します。

上記のコマンドは、実行時に読み取る内容を確認するためにいくつかの簡単なテストを実行するため、重複入力のリスクはありません。以前の場合は、Hすべての行が予約済みスペースに追加されますが、行が一致すると古いスペースが/foo/上書きされます。h次にバッファが変更され、バッファの内容が最後のアドレス指定パターンと一致する場合、条件付きx置換が試みられます。つまり、予約済みスペースの3番目の改行文字を自分で置き換えて結果を印刷してみてください。s///////s/\n/&/3pもし現在の試合のためのスペースを確保してください/foo/。成功すると、tスクリプトは ook 操作を実行し、スクリプトを終了するnotelete タグに分岐します。dl

ただし、2 つの/foo/改行文字と 3 番目の改行文字が予約スペースで一致しない場合は、一致//!gしない場合はバッファーを上書きし/foo/、一致する場合は\n改行文字が一致しない場合はバッファーを上書きします。(したがってそれ/foo/自体が置き換えられます)。この微妙な小さなテストは、長い「いいえ」の間にバッファが不必要に満たされるのを防ぎ、/foo/入力が蓄積されないため、プロセスが迅速に維持されることを保証します。何も存在しない/foo/か失敗した場合は、//s/\n/&/3pバッファが交換され、最後の行を除くすべての行が削除されます。

$!d最後の行(最後の行)は、複数の状況を簡単に処理できるトップダウンスクリプトを作成する方法を簡単に示しています。sed一般的なアプローチは、最も一般的なケースから始まり、最も特定の方向に望ましくないケースを切り取ることであれば、最後に必要な他のデータと共にスクリプトに含めることができるため、極端なケースを処理する方が簡単です。すべてがあなたの周りに囲まれ、あなたが望むデータだけが残ります。しかし、閉ループでこれらの極端なケースを得ることははるかに難しいかもしれない。

だから私が最後に言いたいのは次のとおりです。実際にファイル全体を抽出する必要がある場合は、行サイクルを使用して作業を少し減らすことができます。通常、Nextとnextを使用してください。視野- 彼らが前に進むとき最初行期間。ループ内で閉じたループを冗長に実装するのではなく(sed行ループは単純な読み取りループなので)入力を無分別に収集することが目的であれば、次のようにするのが簡単になります。

sed 'H;1h;$!d;x;...'

...これにより、ファイル全体が収集されます。それ以外の場合、試行は失敗します。


N最後の行の動作に関する注意事項...

テストするツールはありませんが、N読みながらこれを検討してください。所定の位置に編集中のファイルが次に読み取るスクリプトファイルの場合、編集動作は異なります。

おすすめ記事