複数のパターンを一度にsedで置き換える方法は?


231

「abbc」という文字列があり、置き換えたいとします。

  • ab-> bc
  • bc-> ab

2つの置換を試みた場合、結果は希望どおりではありません。

echo 'abbc' | sed 's/ab/bc/g;s/bc/ab/g'
abab

それでは、以下のように置換するためにどのsedコマンドを使用できますか?

echo abbc | sed SED_COMMAND
bcab

編集:実際にはテキストは2つ以上のパターンを持つ可能性があり、どれだけの置換が必要かわかりません。これsedはストリームエディタであり、その置き換えは貪欲であるという回答があったので、そのためにスクリプト言語を使用する必要があると思います。


同じ行で複数の交換を行う必要がありますか?そうでない場合はg、これらのs///コマンドの両方からフラグをドロップするだけで機能します。
Etan Reisner、2014年

あなたは私の質問の要点を逃しました。同じ行で置換を複数回行う必要があることを意味しました。元の入力に対して、ab またはその bc中に複数の一致があります
Etan Reisner、2014年

申し訳ありませんが、@ EtanReisner私は誤解しています。テキストは複数の置換を持つことができます。
DaniloNC 2014年

回答:


342

多分このようなもの:

sed 's/ab/~~/g; s/bc/ab/g; s/~~/bc/g'

~文字列に含まれないことがわかっている文字に置き換えます。


9
GNU sedはnulを処理する\x0ため、で使用できます~~
jthill 2014年

3
g必要な、それが何をするのでしょうか?
Lee

12
@Lee gはグローバル用です。これは、最初のパターン(デフォルトの動作)だけでなく、各行のパターンのすべてのインスタンスを置き換えます。
naught101 2016

1
複数の組み合わせを同時に置き換えることができるoogaの回答のバリエーションについては、私の回答stackoverflow.com/a/41273117/539149を参照してください。
Zack Morris

3
文字列に含まれていないことがわかっていること本番用コードでは、入力について決して仮定しないでください。テストの場合も、テストが実際に正しいことを証明することはありませんが、テストの優れたアイデアは、スクリプト自体を入力として使用することです。
hagello

33

私は常に「-e」で複数のステートメントを使用します

$ sed -e 's:AND:\n&:g' -e 's:GROUP BY:\n&:g' -e 's:UNION:\n&:g' -e 's:FROM:\n&:g' file > readable.sql

これは、すべてのAND、GROUP BY、UNION、およびFROMの前に「\ n」を追加しますが、「&」は一致した文字列を意味し、「\ n&」は一致した文字列を「一致した」の前に「\ n」で置き換えることを意味します」


14

以下は、oogaの回答のバリエーションです。これは、値がどのように再利用されるかを確認する必要なく、複数の検索と置換のペアで機能します。

sed -i '
s/\bAB\b/________BC________/g
s/\bBC\b/________CD________/g
s/________//g
' path_to_your_files/*.txt

次に例を示します。

前:

some text AB some more text "BC" and more text.

後:

some text BC some more text "CD" and more text.

ご了承ください \bは単語の境界示し。これは________、検索が妨げられるのを防ぐためです(UbuntuでGNU sed 4.2.2を使用しています)。単語境界検索を使用していない場合、この手法は機能しない可能性があります。

これは、を削除しs/________//gて追加した場合と同じ結果になることにも注意してください&& sed -i 's/________//g' path_to_your_files/*.txtコマンドの最後にをしてした場合ますが、パスを2回指定する必要はありません。

これの一般的なバリエーションは、jthillが提案するように、ファイルにnullが表示されないことがわかっている場合に、\x0またはその_\x0_代わりに使用することです。________


入力に含まれる可能性のあるものを想定しないことに関する上記のハゲロのコメントに同意します。したがって、個人的には、sedを互いにパイプで接続することを除いて、これが最も信頼できるソリューションであると感じています(sed 's/ab/xy/' | sed 's/cd/ab/' .....
leetbacoon

12

sedストリームエディタです。貪欲に検索して置き換えます。要求したことを行う唯一の方法は、中間置換パターンを使用して、最終的にそれを変更することです。

echo 'abcd' | sed -e 's/ab/xy/;s/cd/ab/;s/xy/cd/'


4

これはあなたのために働くかもしれません(GNU sed):

sed -r '1{x;s/^/:abbc:bcab/;x};G;s/^/\n/;:a;/\n\n/{P;d};s/\n(ab|bc)(.*\n.*:(\1)([^:]*))/\4\n\2/;ta;s/\n(.)/\1\n/;ta' file

これは、準備されてホールドスペース(HS)に保持され、各行に追加されるルックアップテーブルを使用します。一意のマーカー(この場合は\n)が行の先頭に追加され、行の長さ全体にわたって検索に沿ってバンプする方法として使用されます。マーカーが行の終わりに到達すると、プロセスは終了し、ルックアップテーブルに出力され、マーカーは破棄されます。

注:ルックアップテーブルは最初に準備され、2番目の一意のマーカー(この場合は:)は、置換文字列と競合しないように選択されます。

いくつかのコメント付き:

sed -r '
  # initialize hold with :abbc:bcab
  1 {
    x
    s/^/:abbc:bcab/
    x
  }

  G        # append hold to patt (after a \n)

  s/^/\n/  # prepend a \n

  :a

  /\n\n/ {
    P      # print patt up to first \n
    d      # delete patt & start next cycle
  }

  s/\n(ab|bc)(.*\n.*:(\1)([^:]*))/\4\n\2/
  ta       # goto a if sub occurred

  s/\n(.)/\1\n/  # move one char past the first \n
  ta       # goto a if sub occurred
'

テーブルは次のように機能します。

   **   **   replacement
:abbc:bcab
 **   **     pattern

3

次のようにして、単一パターンの発生に対してより簡単な方法を試すことができます。echo 'abbc' | sed 's / ab / bc /; s / bc / ab / 2'

私の出力:

 ~# echo 'abbc' | sed 's/ab/bc/;s/bc/ab/2'
 bcab

パターンが複数出現する場合:

sed 's/\(ab\)\(bc\)/\2\1/g'

~# cat try.txt
abbc abbc abbc
bcab abbc bcab
abbc abbc bcab

~# sed 's/\(ab\)\(bc\)/\2\1/g' try.txt
bcab bcab bcab
bcab bcab bcab
bcab bcab bcab

お役に立てれば !!


2

Tclにはこの機能が組み込まれています

$ tclsh
% string map {ab bc bc ab} abbc
bcab

これは、現在の位置から始まる文字列比較を実行しながら、文字列を一度に1文字ずつ歩くことによって機能します。

Perlの場合:

perl -E '
    sub string_map {
        my ($str, %map) = @_;
        my $i = 0;
        while ($i < length $str) {
          KEYS:
            for my $key (keys %map) {
                if (substr($str, $i, length $key) eq $key) {
                    substr($str, $i, length $key) = $map{$key};
                    $i += length($map{$key}) - 1;
                    last KEYS;
                }
            }
            $i++;
        }
        return $str;
    }
    say string_map("abbc", "ab"=>"bc", "bc"=>"ab");
'
bcab

0

これはawkoogasに基づいていますsed

echo 'abbc' | awk '{gsub(/ab/,"xy");gsub(/bc/,"ab");gsub(/xy/,"bc")}1'
bcab
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.