web-dev-qa-db-ja.com

sedを使用して、一致するパターンの発生のいずれかを編集する

A行で始まる Fred Flintstoneには、何らかの文字列が追加されます。指定されたFred Flintstoneの出現を探して追加します。

このようなパターンのオカレンスのいずれかにこのコマンドを使用するにはどうすればよいですか?私は試した

sed '/Fred Flintstone/ s/$/ someString/2' filename

どうやら上記のものは機能していません。すべてのオカレンスでうまく機能しますが、特定のオカレンスでは機能しません。 (最初または2番目または3番目[いずれか1つ]を交換したい場合)

サンプルFile1:

Fred Flintstone 
Johnson Stone
Fred Flintstone
Fred Flintstone
Michael Clark

必要な出力ファイル1:

Fred Flintstone 
Johnson Stone
Fred Flintstone someString
Fred Flintstone
Michael Clark
6
DazzlerJay

sedに言及しましたが、これらはawk- yタスクの一種です:

awk -v pat="Fred Flintstone" '$0 ~ pat {count++;\
               if (count == 2) { $0 = $0" someString" ;} ;}; 1' file.txt
  • -v pat="Fred Flintstone"は、pat式内で使用される変数awkとして一致する正規表現パターンを保存します

  • $0 ~ patは、一致するレコードをpatに対してチェックします。一致した場合、count変数は1増加し、countが2の場合、レコードは現在のコンテンツにsomeString{count++; if (count == 2) { $0 = $0" someString" ;} ;})を加えたものとしてリセットされます。

  • 1はイディオムです。そのままtruthy、すべてのレコードが印刷されます

例:

% cat file.txt
Fred Flintstone
Johnson Stone
Fred Flintstone
Fred Flintstone
Michael Clark

% awk -v pat="Fred Flintstone" '$0 ~ pat {count++; if (count == 2) { $0 = $0" someString" ;} ;}; 1' file.txt
Fred Flintstone
Johnson Stone
Fred Flintstone someString
Fred Flintstone
Michael Clark
7
heemayl

この単純なsedコマンドを使用すると、ループを使用せずに(ブランチツーエンドを使用)、GNU拡張子を必要とせずに、またはファイル全体を一度に読み取らずに、選択的に変更を行うことができます。

sed -r '/Fred Flintstone/ {x; s/$/#/; /^#{2}$/ {x; s/.*/& someString/; b}; x}'

説明:

  • -r-拡張正規表現を使用
  • /Fred Flintstone/-このパターンに一致する行の場合:
    • x-パターンスペースを交換し、スペースを保持します(カウンターをアクティブにします)
    • s/$/#/-カウンターに文字を追加します
    • /^#{2}$/-カウンターの長さが2の場合(任意の値に置換)
      • xパターンスペースを交換し、スペースを保持します(カウントされた入力行をアクティブにします)
      • s/.*/& someString/-目的の行に文字列を追加します
      • b-印刷できるように、この行の処理の最後までスキップします
    • x-パターンスペースを交換し、スペースを保持します(文字列に一致するがカウントには一致しない行をアクティブにします)

説明のインデントレベルは、中括弧のネストのレベルを示しています。

他のすべての行は処理せずに通過し、印刷されます。

5

ファイル全体をメモリに丸memoryみせずにsedで実行する方法を次に示します。

  • firstパターンの出現を見つける
  • 次の行Nをパターンスペースに追加します
  • パターンのsecondオカレンスの置き換えを試みます
  • 一致が失敗した場合、分岐して戻って別の行を追加しますTa

$qは、一致せずにファイルの終わりに達した場合にループを終了します。

そう

sed '/Fred Flintstone/ {:a; $q; N; s//& someString/2; Ta;}' File1
Fred Flintstone 
Johnson Stone
Fred Flintstone someString
Fred Flintstone
Michael Clark

TはGNU拡張機能ですが、POSIX sedでもt; baを使用して同じことができます


うーん...これについてもう少し考えた後、実際に新しいテキストを他のすべての出現に追加します-2番目だけでなく。本当に使用する必要があるのが2番目のオカレンスのみの場合、私がそれを行うことができる唯一の方法は次のとおりです。

  • アドレスし、最初のインスタンスを一意の文字列で置き換えます
  • 新しい最初のインスタンスのアドレス指定と変更
  • アドレスし、一意の文字列を元のパターンに置き換えます

GNU sedは、パターンのfirstインスタンスに対処するためのトリックを提供します

0,/pattern/

そう

sed -e '0,/Fred Flintstone/ s//Barney Rubble/' \
    -e '0,/Fred Flintstone/ s//& someString/' \
    -e '0,/Barney Rubble/ s//Fred Flintstone/
' File1
Fred Flintstone
Johnson Stone
Fred Flintstone someString
Fred Flintstone
Michael Clark

Edの使用を気にしない場合は、最初の行に移動して、あるインスタンスから次のインスタンスに前方一致することにより、2番目のインスタンスをより直接アドレス指定できます。

ed -s File1 << \EOF                                                                                                              
1;#
/Fred Flintstone/,/Fred Flintstone/ s//& someString/
,p
q
EOF
Fred Flintstone
Johnson Stone
Fred Flintstone someString
Fred Flintstone
Michael Clark

またはワンライナーとして

printf '1;#\n/Fred Flintstone/,/Fred Flintstone/ s//& someString/\n,p\n' | ed -s File1

,pwに置き換えることにより、edバージョンをインプレースにできます。

4
steeldriver

ex、POSIX指定ファイルエディター の別の使用例

(これはviの前身であり、依然としてviの一部です。)

printf '%s\n' '0/Fred Flintstone///s/$/ someString/' x | ex filename

ここでの魔法は、Sed、Awkおよび同様のツールとは異なり、exはすべての行でコードを実行しないことです。カーソルを動かしたり、コマンドを与えたりできます。

この場合、行番号0(最初の行のFred Flintstoneが考慮されるようにします)を指定し、その後にファイル一致の最初の行を参照する正規表現/Fred Flintstone/を続けますその正規表現の後に別の正規表現//が続きます。空の場合、最後の正規表現を再利用するため、ファイル内のsecond行を参照します一致する;そして、すでに知っているsコマンドを使用します。

xコマンドは、変更を保存して終了することを意味します。

printfを使用して、コマンドをexに送ります。

2
Wildcard