改行文字を含む文字列を置き換えます


10

bashシェル、以下のもののような行を持つファイルに

first "line"
<second>line and so on

の1つ以上の出現をに置き換え"line"\n<second>other characters、毎回取得したいと思います。

first other characters line and so on

両方などの特殊文字で文字列を置換する必要があり、私はそう"<して改行文字と。

他の回答の間で検索したところsed、コマンドの右側(other characters文字列)の改行は受け入れられるが、左側は受け入れられないことがわかりました。

またはでこの結果を取得する方法(これよりも簡単)はありますか?sedgrep


Macで作業していますか?\n私は尋ねる、なぜあなたが作るewline文です。他のほとんどのsがそのエスケープを右側で拒否するs//\n/でしょうが、GNU sedでできるようにできるかどうか尋ねられることはほとんどありませんsed。それでも、\nエスケープはどのPOSIXでも左側で機能し、同じように効果があるので常にsed移植可能に翻訳できます。y/c/\n/s/c/\n/g
mikeserv 2014年

回答:


3

3つの異なるsedコマンド:

sed '$!N;s/"[^"]*"\n<[^>]*>/other characters /;P;D'

sed -e :n -e '$!N;s/"[^"]*"\n<[^>]*>/other characters /;tn'

sed -e :n -e '$!N;/"$/{$!bn' -e '};s/"[^"]*"\n<[^>]*>/other characters /g'

3つとも、基本的なs///ubstitutionコマンドに基づいています。

s/"[^"]*"\n<[^>]*>/other characters /

また、sedsはエッジケースで出力が異なる傾向があるため、最後の行の処理に注意を払います。これは、最後$!では!ないすべての行に一致する住所の意味です$

また、すべてNextコマンドを使用して、次の入力行を\newline文字に続くパターンスペースに追加します。sedしばらく使用している人は誰でも、\newlineキャラクターに依存することを学びます。これを取得する唯一の方法は、明示的にそこに置くことです。

3つすべては、アクションを実行する前に、可能な限り少ない入力を読み取るように試みsedます。そうする前に、入力ファイル全体を読み取る必要がない場合と同じようにすぐに動作します。

すべてを実行しますがN、3つすべてが再帰の方法が異なります。

最初のコマンド

最初のコマンドは非常に単純なN;P;Dループを採用しています。これらの3つのコマンドはPOSIX互換に組み込まれてsedおり、お互いをうまく補完します。

  • N-すでに述べたNように、挿入された\newline区切り文字に続いて、パターン入力スペースにext入力行を追加します。
  • P-のようなp; それはP、パターン・スペースをrints -だけのアップに最初に発生\newline文字。したがって、次の入力/コマンドが与えられます:

    • printf %s\\n one two | sed '$!N;P;d'
  • sed P1つだけをリントします。ただし、...

  • D-のようなd; それはD、パターン・スペースをeletesし、別のライン・サイクルを開始します。とは異なり d、パターンスペースでD最初に発生する\newline までを削除します。\newline文字に続くパターンスペースがさらにある場合は、sed残っているものから次の行サイクルを開始します。場合はd、前の例では、置き換えられたD、例えば、sedでしょうP両方RINT 12を

このコマンドは、ubstitutionステートメントに一致しない行に対してのみs///再帰します。ubstitutionはで追加されたs///ewlineを削除するため、pattern-spaceを選択しても何も残っていません。\nNsed D

Pおよび/またはD選択的に適用するためにテストを行うことができますが、その戦略によりよく適合する他のコマンドがあります。再帰は置換規則の一部のみに一致する連続する行を処理するように実装されているため、置換の両端に一致する連続する行のシーケンスはs///うまく機能しません。

この入力が与えられた場合:

first "line"
<second>"line"
<second>"line"
<second>line and so on

...印刷します...

first other characters "line"
<second>other characters line and so on

ただし、

first "line"
second "line"
<second>line

...大丈夫。

2番目のコマンド

このコマンドは3番目のコマンドとよく似ています。どちらも:b牧場/ tエストのラベルを使用しており(Joeseph R.の回答もここに示されています、特定の条件が与えられた場合は再帰します。

  • -e :n -e-移植可能なsedスクリプトは:\newlineまたは新しいインライン-executionステートメントのいずれかでラベル定義を区切ります。
    • :n-という名前のラベルを定義しnます。これは、bnまたはを使用していつでも戻すことができますtn
  • tn- ラベルが定義された後、または最後に呼び出されてからestsが成功した場合、testコマンドは指定されたラベルに戻ります(または、何も指定されていない場合は、現在のラインサイクルのスクリプトを終了します)s///t

このコマンドでは、一致する行に対して再帰が発生します。sedパターンが他の文字で正常に置き換えられた場合sed:nラベルに戻って再試行します。s///置換が実行されない場合、sedパターンスペースが自動印刷され、次のラインサイクルが開始されます。

これは連続したシーケンスをよりよく処理する傾向があります。最後の1つが失敗した場合は、次のように出力されます。

first other characters other characters other characters line and so on

3番目のコマンド

述べたように、ここでのロジックは最後のものと非常に似ていますが、テストはより明確です。

  • /"$/bn-これはsedのテストです。branchコマンドはこのアドレスの関数であるため、ewlineが追加され、パターンスペースが二重引用符で終了した後にsedのみ、b牧場に戻ります。:n\n"

少しの間で行われるようにありますNし、b可能な限り-このようにsed非常に迅速に次の行は、あなたのルールに一致することができないことを保証するために必要な多くの入力として正確に収集することができます。s///ubstitutionは、ここではglobalフラグを使用するという点で異なります。したがって、必要なすべての置換を一度に行います。同一の入力が与えられた場合、このコマンドは最後まで同じように出力します。


ささいな質問で申し訳ありませんがDATA、テキスト入力の意味と方法は何ですか?
BowPark 2014年

@BowPark-この例で<<\DATA\ntext input\nDATA\nはベイクインさsedれていますが、それはヒアドキュメントでシェルから渡されたテキストのみです。sed 'script' filenameまたはと同様に機能しますprocess that writes to stdout | sed 'script'。それは役に立ちますか?
mikeserv 2014年

はい、ありがとうございます!なぜDすべての変更された行がないと二重になるのですか?(あなたはそれを必要に応じて使用しました;多分私はsedよく知りません)
BowPark

1
@BowPark-省略しDD場合Dは2倍になります。それ以外の場合は、出力から2倍に見えるものが削除されるためです。私は編集を行ったところです-そしてすぐにそれをさらに拡張するかもしれません。
mikeserv 2014年

1
@BowPark-わかりました。更新し、オプションを提供しました。読みやすくなり、理解しやすくなります。私も明示的に対処しましたD
mikeserv 2014年

7

まあ、私はいくつかの簡単な方法を考えることができますが、どちらも含まgrepれません(とにかく置換を行いません)またはsed

  1. Perl

    出現箇所をで置き換えるに"line"\n<second>other characters、次を使用します。

    $ perl -00pe 's/"line"\n<second>/other characters /g' file
    first other characters line and so on
    

    または、の連続する複数の発生を"line"\n<second>1つとして扱い、それらすべてを1つのに置き換えるには、次のコマンドをother characters使用します。

    perl -00pe 's/(?:"line"\n<second>)+/other characters /g' file
    

    例:

    $ cat file
    first "line"
    <second>"line"
    <second>"line"
    <second>line and so on
    $ perl -00pe 's/(?:"line"\n<second>)+/other characters /g' file
    first other characters line and so on
    

    これ-00により、Perlは「段落モード」でファイルを読み取ります。つまり、「行」はの\n\n代わりに定義されます\n。基本的に、各段落は行として扱われます。したがって、置換は改行全体で一致します。

  2. awk

    $  awk -v RS="\n\n" -v ORS="" '{
          sub(/"line"\n<second>/,"other characters ", $0)
          print;
        }' file 
    first other characters line and so on
    

    同じ基本的な考え方ですが、レコードセパレーター(RS)をに設定して\n\nファイル全体を丸め、次に出力レコードセパレーターを何にも設定しません(そうでない場合、追加の改行が出力されます)。次に、sub()関数を使用して置き換えます。


2
@mikeserv?どれ?2番目は想定されているとおり、OPは「1つ以上のオカレンスを置き換える」ことを望んでいるため、段落を食べることが期待されるかもしれません。
terdon

非常に良い点です。私はもっ​​と集中して毎回取得していると思いますが、それが発生ごとに1つの置換であるか、発生のシーケンスごとに1つの置換であるかは不明です... @BowPark?
mikeserv 2014年

発生ごとに1回の交換が必要です。
BowPark、2014年

@BowPark OK、それから最初のperlアプローチまたはawkの両方が機能するはずです。それらはあなたに望ましい出力を与えませんか?
terdon

動作します。ありがとうございます。3行目はであるawk必要がありますprint;}' file。私はPerlを避け、できればを使用する必要がありsedます。とにかく、良い代替案を提案しました。
BowPark、2014年

6

ファイル全体を読み取り、グローバル置換を行います。

sed -n 'H; ${x; s/"line"\n<second>/other characters /g; p}' <<END
first "line"
<second> line followed by "line"
<second> and last
END
first other characters  line followed by other characters  and last

はい。動作しますが、複数回発生した場合はどうなりますか?
BowPark、2014年

ええ、そうです。修正済み
グレン・ジャックマン、2014年

1
もう一度nitpickに申し訳ありません${cmds}が、GNU固有です-他sedのほとんどのは、との間に\newlineまたは-e改行が必要です。あなたは完全にブラケットを避けることができる-と移植性-とさえ余分な挿入を避けるように最初の行にewline文字を:p}\nsed 'H;1h;$!d;x;s/"line"\n<second>/other characters /g'
mikeserv

私はそれをテストしました、そしてそれはポータブルではないようです。出力の先頭に余分な改行を出力しますが、GNUでは結果は正しいです。
BowPark、2014年

先頭の改行を削除するには:sed -n '1{h;n};H; ${x; s/"line"\n<second>/other characters /g; p}'-ただし、これは維持できなくなっています。
グレン・ジャックマン、2014年

3

これは、複数の連続したオカレンスがある場合に機能するglennの回答の変形です(GNUでsedのみ機能します)。

sed ':x /"line"/N;s/"line"\n<second>/other characters/;/"line"/bx' your_file

これ:xは分岐のラベルにすぎません。基本的に、これが行うことは、置換後の行をチェックし、それがまだと一致する場合"line"は、:xラベルに戻り(つまり、何をするかbx)、別の行をバッファーに追加して処理を開始することです。


@mikeserv意味を具体的に説明してください。それは私のために働いた。
ジョセフR.

@mikeservすみません、何を言っているのか本当にわかりません。上記のコード行を端末にコピーして戻したところ、正しく動作しました。
ジョセフR.

1
撤回-これは明らかにsed、非POSIXラベル処理を、ラベル宣言の区切り文字としてスペースを受け入れるのに十分な距離で行うGNU で機能します。ただし、他のものsedはそこで失敗し、失敗しNます。GNU sedは、POSIXガイドラインに違反しNて、最後の行で終了する前にパターンスペースを印刷しますが、POSIXでは、Nコマンドが最後の行で読み取られた場合、も印刷されないことを明確にしています。
mikeserv 2014年

投稿を編集してGNUを指定した場合、私の投票を取り消して、これらのコメントを削除します。また、GNUのvコマンドが1つおきに壊れるsedが、GNUバージョン4以降では何もしないコマンドについても学ぶ価値があります。
mikeserv 2014年

1
その場合、もう1つ提供しますsed -e :x -e '/"line"/{$!N' -e '};s/"line"\n<second>/other characters/;/"line"/bx'。これは次のように移植できます。
mikeserv 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.