web-dev-qa-db-ja.com

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

'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

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

157
DaniloNC

多分このようなもの:

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

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

252
ooga

次に、 oogaのanswer のバリエーションを示します。これは、値の再利用方法を確認することなく、複数の検索と置換のペアに対して機能します。

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を使用しています)。 Wordの境界検索を使用していない場合、この手法は機能しない可能性があります。

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

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

10
Zack Morris

私はいつも "-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'で置き換えます。 '

sedはストリームエディタです。欲張って検索して置き換えます。あなたが要求したことをする唯一の方法は、中間置換パターンを使用し、それを最後に戻すことです。

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

6
kuriouscoder

これはうまくいくかもしれません(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)が行の先頭に追加され、行の長さ全体に渡って検索を進めるための方法として使用されます。マーカーが行の終わりに達すると、プロセスは終了し、ルックアップテーブルに出力され、マーカーは破棄されます。

N.B.ルックアップテーブルは最初に作成され、置換文字列と衝突しないように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
4
potong

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
2
glenn jackman

これはoogasに基づいたawkですsed

echo 'abbc' | awk '{gsub(/ab/,"xy");gsub(/bc/,"ab");gsub(/xy/,"bc")}1'
bcab
0
Jotne

以下のように試みることができる単一パターン発生のためのより簡単なアプローチであるかもしれません。 sedの/ 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

お役に立てれば !!

0
dst_91