SEDを使用して最初の一致後に行全体を検索して置き換えますか?

SEDを使用して最初の一致後に行全体を検索して置き換えますか?

次の形式を使用するファイルがあります。

M104 S200
M107
M104 S250
M110
M104 S275

M104私は2番目の項目を一致させ、他の項目も含めるように行全体を書き換える方法を見つけようとしていますM104

頑張った

sed 's/^M104/s/.*/M104 S300/' file - but it replaces all instances of M104
sed '0,/M104/s//M104 S200/' file - leaves the trailing text on the line
  • 他の変更により予期しない結果が発生しました。助けが必要ですか? ;)

答え1

Perlがある場合は、次のようにします。

$ perl -pe 'if (/M104/) { $i++; if($i == 2) { $_ = "M104 S999\n" } } ' test.txt
M104 S200
M107
M104 S999
M110
M104 S275

つまり/M104/、一致した場合は増やしてから$i2と同じであることを確認し$i、そうであれば現在の行を置き換えます(末尾の改行を含む\n)。

s/^M104.*/.../そこでsedなどの代替品を使用したい場合は、最後の部分でそれを行うことができます。

-i[extension]Perlには、バックアップファイルの有無にかかわらず内部変更を実行できるsedに似たオプションがあります。

または、環境変数を介してキー値を渡します(スクリプトが簡単になります)。

n=2 repl="S999" perl -pe 'if (/M104/) { $i++; if($i == $ENV{n}) { $_ = "M104 " . $ENV{repl} . "\n" } } ' test.txt

(または、たとえばネストよりも接続を好む場合。perl -pe '/M104/ and $i++ and $i == 2 and $_ = "M104 S999\n" ' test.txt


またはawkから:

$ awk '/M104/ { i++; if (i == 2) $0 = "M104 S999"; } 1' test.txt

ただし、awkには標準の内部オプションはありません。

答え2

あなたの(失敗した)試みは、あなたのバージョンがsed範囲の0開始を許可することを意味します(すべてsedがこれを行うわけではありません)。それでは試してみてください。

sed -rn '0,/^M104/{p;b;}; 0,/(^M104).*/ s//\1 S300/; p' file
M104 S200
M107
M104 S300
M110
M104 S275

アイデアは、アドレス範囲を2回適用することです。最初の範囲は、最初の一致まで変更されていない行を検索して印刷します。 2番目の範囲は、次のゲームを置き換えます。同様に、sed2番目の範囲の開始アドレスが最初の範囲で使用されている場合、すべてのバージョンで2番目の範囲の開始アドレスを許可するわけではありません。

答え3

使用する場合牛に似た一種の栄養 sed、この-zオプションを使用すると、1つのバッファでファイル全体を処理し、sそれを数値フラグコマンドの数値フラグに置き換えることができます。N最初の発生:

sed -zE 's/(^|\n)M104[^\n]*/\1M104 S300/2'

2番目の項目を置き換えるように指示します2sed

-E改行またはファイルの先頭を一致させるために拡張正規表現(オプション)を使用しており、\1改行を維持するために一致()を再挿入します。

これ持ち運べるバージョン-zオプションは使用できず、[^\n]「改行文字を除くすべて」には許可されていないため、保持バッファを使用して行を収集し、行に印刷可能な文字のみを含めることを期待しています。

sed -E 'H;1h;$!d;x;s/(^|\n)M104[[:print:]]*/\1M104 S300/2'

また、非常に大きなファイルの場合、バッファが大きすぎる可能性があります。

関連情報