ファイル全体をパターン空間に読み込むことは、改行などを変更するのに役立ちます。そして、以下を提案する多くの例があります。
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つの論理データブロックを操作するように設計されています。
全体のファイルブロックをよりよく処理する同様のツールがたくさんあります。たとえば、ed
andはほとんどの作業を実行できますが、それ以外にははるかに多くの作業を実行できますが、出力に変換中に入力ストリームで作業するだけでなく、ファイルシステムでも作業を実行します。一時バックアップファイルを保持します。その操作は必要に応じてディスクにバッファリングされ、ファイルの最後で突然終了しません。ex
sed
sed
(バッファ圧力下でははるかに頻繁に破裂しません)。またsed
、ライン表示、元に戻す、名前付きバッファ、結合など、ストリーミングコンテキストでは理解できない多くの便利な機能も提供します。
sed
最大の利点は、データを読み取った後すぐに迅速かつ効率的かつ継続的に処理できることです。ファイルを飲み込むと捨てます。そして上記の最後の行の問題だけでなく、バッファオーバーフローやパフォーマンスの低下など、極端な場合に問題が発生する傾向があります。解析するデータの長さが長くなるにつれて、一致を列挙すると正規表現エンジンの処理時間が長くなります。指数関数的に。
最後のポイントに関して、注:例s/a/A/g
は単なる例であり、入力から収集したい実際のスクリプトではないかもしれませんが、慣れるのに時間を費やす価値があることがわかりますy///
。g
ある文字をグローバルに別の文字に頻繁に置き換える場合、これはy
非常に便利です。これは代替ではなく変換であり、正規表現を暗示しないため、はるかに高速です。後者は、空のアドレスを維持し、繰り返す場合にも便利です。//
なぜなら、空のアドレスには影響しませんが、影響を受ける可能性があるからです。とにかくy/a/A/
これは同じことを行うより簡単な方法です。たとえば、1y/aA/Aa/
行のすべての大文字/小文字を置き換えるなどの置き換えも可能です。
また、あなたが説明する行動は何らかの形で起こるべき行動ではないことに注意してください。
info sed
GNUから一般的なバグレポート部分:
N
コマンドは最後の行にありますsed
ほとんどのExitバージョンは、コマンドがファイルの最後の行で実行されたときに何も印刷しません。もちろん、コマンドスイッチが指定されていない限り、N
GNUはsed
終了前にモードスペースを印刷します。-n
この選択は意図的に設計されています。たとえば、の動作は、
sed N foo bar
fooの行数が偶数か奇数かによって異なります。または、パターンマッチング後に次の数行を読むスクリプトを書くとき、伝統的な実装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
、、、、、、w
にt
も同様に適用されますa
。i
c
(多分今は忘れてしまった内容がもっとあるだろう)。ほとんどの場合、次のように書くこともできます。
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
... ほとんどの命令と同様に、t
est命令は戻りsed
レジスタをフラッシュするラインサイクルに依存し、ラインサイクルがここでほとんどの操作を実行できるようになるためです。これは、ファイルを読み取るときに発生するもう1つのトレードオフです。行サイクルは再フラッシュされないため、多くのテストが異常に機能します。
上記のコマンドは、実行時に読み取る内容を確認するためにいくつかの簡単なテストを実行するため、重複入力のリスクはありません。以前の場合は、H
すべての行が予約済みスペースに追加されますが、行が一致すると古いスペースが/foo/
上書きされます。h
次にバッファが変更され、バッファの内容が最後のアドレス指定パターンと一致する場合、条件付きx
置換が試みられます。つまり、予約済みスペースの3番目の改行文字を自分で置き換えて結果を印刷してみてください。s///
//
//s/\n/&/3p
もし現在の試合のためのスペースを確保してください/foo/
。成功すると、t
スクリプトは ook 操作を実行し、スクリプトを終了するn
otelete タグに分岐します。d
l
ただし、2 つの/foo/
改行文字と 3 番目の改行文字が予約スペースで一致しない場合は、一致//!g
しない場合はバッファーを上書きし/foo/
、一致する場合は\n
改行文字が一致しない場合はバッファーを上書きします。(したがってそれ/foo/
自体が置き換えられます)。この微妙な小さなテストは、長い「いいえ」の間にバッファが不必要に満たされるのを防ぎ、/foo/
入力が蓄積されないため、プロセスが迅速に維持されることを保証します。何も存在しない/foo/
か失敗した場合は、//s/\n/&/3p
バッファが交換され、最後の行を除くすべての行が削除されます。
$!d
最後の行(最後の行)は、複数の状況を簡単に処理できるトップダウンスクリプトを作成する方法を簡単に示しています。sed
一般的なアプローチは、最も一般的なケースから始まり、最も特定の方向に望ましくないケースを切り取ることであれば、最後に必要な他のデータと共にスクリプトに含めることができるため、極端なケースを処理する方が簡単です。すべてがあなたの周りに囲まれ、あなたが望むデータだけが残ります。しかし、閉ループでこれらの極端なケースを得ることははるかに難しいかもしれない。
だから私が最後に言いたいのは次のとおりです。実際にファイル全体を抽出する必要がある場合は、行サイクルを使用して作業を少し減らすことができます。通常、N
extとn
extを使用してください。視野- 彼らが前に進むとき最初行期間。ループ内で閉じたループを冗長に実装するのではなく(sed
行ループは単純な読み取りループなので)入力を無分別に収集することが目的であれば、次のようにするのが簡単になります。
sed 'H;1h;$!d;x;...'
...これにより、ファイル全体が収集されます。それ以外の場合、試行は失敗します。
N
最後の行の動作に関する注意事項...
テストするツールはありませんが、
N
読みながらこれを検討してください。所定の位置に編集中のファイルが次に読み取るスクリプトファイルの場合、編集動作は異なります。
答え2
N
コマンドはパターンマッチング$!
(最後の行ではない)の前に来て、操作を実行する前にsedが終了するため失敗します。
窒素
パターンスペースに改行文字を追加したら、パターンスペースに次の入力行を追加します。これ以上入力がない場合、sed はコマンドを処理せずに終了します。。
N
b
パターンの後ろとコマンドをグループ化するだけで、1行の入力を処理するように簡単に変更できます(実際には実際にははるかに明確です)。
sed ':a;$!{N;ba}; [commands...]'
仕組みは次のとおりです。
:a
「a」というラベルを作成します。$!
最後の行ではない場合N
パターン空間に次の行を追加(次の行がない場合は終了)と分岐ba
(移動)ラベル「a」
残念ながら、GNU拡張に依存しているので移植性はありませんが、@mikeservが提案した後の代替案は移植性があります。
sed 'H;1h;$!d;x; [commands...]'
答え3
@mikeservが徹底的に説明したように、Nはこれには適していません。
このフラグメントはファイル全体を蓄積し、スクリプトの残りの部分のプレフィックスとして使用できます。
H;$!d;x;s/^\n//
最後の行を読み取るまで、Hを使ってファイルを蓄積します。
* GNU sedでテストされた使用例)欠落している末尾\ nを参照してください。
$ printf 'a\nb\nc' | sed -e 'H;$!d;x;s/^\n//' -e 's/^/[/;s/$/]/'
[a
b
c]$ echo $?
0