sedを使用して複数行の文字列を置き換える方法は?

sedを使用して複数行の文字列を置き換える方法は?

\nusingを置き換えるためにパターンを追加すると、一致しないsedことがわかりました。例:

$ cat > alpha.txt
This is
a test
Please do not
be alarmed

$ sed -i'.original' 's/a test\nPlease do not/not a test\nBe/' alpha.txt

$ diff alpha.txt{,.original}

$ # No differences printed out

どのように動作させることができますか?

答え1

最も簡単な呼び出しでsed、それは一つパターン空間のテキスト行。\n入力から区切られたテキストの1行。パターンスペースには1行もありません\n。だから正規表現が何も見つからないのです。

複数の行をパターン空間に読み込んで驚くほどうまく操作できますが、通常よりもはるかに多くの努力が必要です。 Sedにはこの種の操作を許可するコマンドセットがあります...ここにリンクがありますsed コマンドの要約。これは私が見つけたものの中で最高であり、私を興奮させます。

しかし、sedのマイクロコマンドを使い始めたら、「一行」の概念を忘れてください。感じを得るまで、構造化されたプログラムのように配置すると便利です。とてもシンプルながらもユニークです。テキスト編集の「アセンブリ言語」と考えればいいのです。

要約:簡単な作業にはsedを使用してください。たぶんそれ以上であるかもしれませんが、一般的に1行を使用するよりも多くの場合、ほとんどの人は他のものを好みます
。オプションは(私はsedを使用しますが、それは私がPerlをよく知らないからです)。


sed '/^a test$/{
       $!{ N        # append the next line when not on the last line
         s/^a test\nPlease do not$/not a test\nBe/
                    # now test for a successful substitution, otherwise
                    #+  unpaired "a test" lines would be mis-handled
         t sub-yes  # branch_on_substitute (goto label :sub-yes)
         :sub-not   # a label (not essential; here to self document)
                    # if no substituion, print only the first line
         P          # pattern_first_line_print
         D          # pattern_ltrunc(line+nl)_top/cycle
         :sub-yes   # a label (the goto target of the 't' branch)
                    # fall through to final auto-pattern_print (2 lines)
       }    
     }' alpha.txt  

ここで同じスクリプトがはっきりと読みやすく難しい内容に圧縮されていますが、一部ではこれをあいまいに呼ぶこともあります。一行

sed '/^a test$/{$!{N;s/^a test\nPlease do not$/not a test\nBe/;ty;P;D;:y}}' alpha.txt

これは私のコマンド「チートシート」です

:  # label
=  # line_number
a  # append_text_to_stdout_after_flush
b  # branch_unconditional             
c  # range_change                     
d  # pattern_delete_top/cycle          
D  # pattern_ltrunc(line+nl)_top/cycle 
g  # pattern=hold                      
G  # pattern+=nl+hold                  
h  # hold=pattern                      
H  # hold+=nl+pattern                  
i  # insert_text_to_stdout_now         
l  # pattern_list                       
n  # pattern_flush=nextline_continue   
N  # pattern+=nl+nextline              
p  # pattern_print                     
P  # pattern_first_line_print          
q  # flush_quit                        
r  # append_file_to_stdout_after_flush 
s  # substitute                                          
t  # branch_on_substitute              
w  # append_pattern_to_file_now         
x  # swap_pattern_and_hold             
y  # transform_chars                   

答え2

perl代わりに使用してくださいsed

$ perl -0777 -i.original -pe 's/a test\nPlease do not/not a test\nBe/igs' alpha.txt
$ diff alpha.txt{,.original}
2,3c2,3
< not a test
< Be
---
> a test
> Please do not

-pi -e標準の「位置から置換」コマンドラインシーケンス。 -0777はPerlにファイル全体を読み取らせます。バラよりパードックフェレンこれについて詳しく学んでください。

答え3

\n私の考えでは、シンボルを別のシンボルに置き換えて、通常どおりに作業する方が良いと思います。

未使用のソースコードの例:

cat alpha.txt | sed -e 's/a test\nPlease do not/not a test\nBe/'

次のように変更できます。

cat alpha.txt | tr '\n' '\r' | sed -e 's/a test\rPlease do not/not a test\rBe/'  | tr '\r' '\n'

あなたが知らない人がいるなら、\nUNIX行の終わりは\r\n- windows、\r- classic Mac OSです。通常のUNIXテキストではシンボルを使用しないため、\rこの場合はシンボルを使用するのが安全です。

いくつかの外国のシンボルを使用して一時的に\ nを置き換えることもできます。たとえば、- \f(フォームフィード記号)です。より多くの記号を見つけることができます。ここ

cat alpha.txt | tr '\n' '\f' | sed -e 's/a test\fPlease do not/not a test\fBe/'  | tr '\f' '\n'

答え4

すべての状況を考慮し、ファイル全体を飲み込んでください。おそらく最速の方法です。

基本的な構文は次のとおりです。

sed -e '1h;2,$H;$!d;g' -e 's/__YOUR_REGEX_GOES_HERE__...'

ファイルが非常に大きい場合、ファイル全体を飲み込むことはオプションではないかもしれません。この状況について、ここにある他の答えは、より小さなメモリ空間で動作することを保証するカスタムソリューションを提供します。

他のすべてのハッキングケースの場合、生の正規表現引数を-e '1h;2,$H;$!d;g'sedに追加すると操作が完了します。

例えば

$ echo -e "Dog\nFox\nCat\nSnake\n" | sed -e '1h;2,$H;$!d;g' -re 's/([^\n]*)\n([^\n]*)\n/Quick \2\nLazy \1\n/g'
Quick Fox
Lazy Dog
Quick Snake
Lazy Cat

-e '1h;2,$H;$!d;g'効果は何ですか?

1、、、2,$部分は、次の$!コマンドが実行される行を制限する行指定子です。

  • 1:最初の行だけ
  • 2,$: 2行目から始まるすべての行
  • $!: 最後の行を除くすべての行

拡張すると、これはN本の入力ラインの各ラインで発生します。

  1: h, d
  2: H, d
  3: H, d
  .
  .
N-2: H, d
N-1: H, d
  N: H, g

このgコマンドは行指定子を提供しませんが、前のdコマンドには「という特別な句があります。次のサイクルを開始してください。「これは、g最後の行を除くすべての行で実行されるのを防ぎます。

各コマンドの意味は次のとおりです。

  • hsが続く各行の最初の行は、対応するH入力行をsedsにコピーします。スペアスペース。 (任意のテキストバッファを考えてみてください。)
  • 次に、dそのラインが出力に書き込まれないように各ラインを削除します。これスペアスペースしかし、それでも維持されます。
  • 最後に、最後の行からg次から復元します。スペアスペースこれsedにより、(一度に1行ではなく)入力全体に対して正規表現を実行できるため、\nsと一致することができます。

関連情報