awk正規表現で正規表現グループを参照する方法は?

awk正規表現で正規表現グループを参照する方法は?

awk正規表現で正規表現グループを参照する方法は?たとえば、正規表現グループがある場合は、(\w)後で同じ正規表現でそれらを参照するのですか(\w)\1? awkはこの機能をサポートしていますか?以下の例は機能しません。

# In this example, I want to change aa to aaa and cc to ccc.
echo ab aa cc de mn | gawk '{print gensub(/(\w)\1/, "\\1\\1\\1", "g")}'
# The result is: ab aa cc de mn
# The expected result is: ab aaa ccc de mn

答え1

busyboxの実装awkは、私が知っている唯一の逆参照をサポートする実装です。と拡張もgawkサポートします。gensub()\w

sub()と同様に、標準では値が1()の文字であり、その文字と一致する必要がありますが、(まあ)代わりにおよび代わりに使用する必要がありgsub()ます。"..."/.../\\1\1awk"\1"^A/\1/"\\1"以前は) は POSIX では指定されません。また、POSIX EREには逆参照はありません。この機能はBREにはありますが、EREにはありません。

$ echo ab aa cc de mn | busybox awk '{print gensub("(\\w)\\1", "\\1\\1\\1", "g")}'
ab aaa ccc de mn

busyboxはサポートされawkていませんが、グローバリゼーション、ロケールに関係なく(と同じ)一致し、\wマルチバイト文字をサポートしません。a-zA-Z0-9_[[:alnum:]]

$ echo ee éé | busybox awk '{print gensub("(\\w)\\1", "\\1\\1\\1", "g")}'
eee éé

sed標準ユーティリティの場合、通常は作業に使用されます。

sed 's/\([[:alnum:]_]\)\1/&\1/g'

sed正規表現は次のとおりです。基本的な逆参照された正規表現をサポートします。一部のsed実装サポート拡大するPOSIXが標準の次のメジャーバージョンで指定するか、またはを含む正規表現はまだ参照を返しません(キャプチャリング-rグループの代替エントリが指定されていても)。 GNUとbusyboxは逆参照をサポートしていますが、FreeBSDはサポートしていません。-E-Essed-Esed

答え2

$ echo ab aa cc de mn | perl -pe 's/(\w)\1/\1\1\1/g'
ab aaa ccc de mn

時にはawkはできませんが、Perlはできることがあることを受け入れなければなりません。

awk良い点は、あなたが十分に熟練していて、gensub逆参照をしたい場合は、perlそれが非常に簡単であるということです。つまり、awkを書ければperlも書けるという意味だ。

答え3

awkこれはおそらく質問の範囲外ですが、逆参照がサポートされていないのは、awk常に次のものを使用するためです。本物正規表現、実装可能な式再帰なし有限状態機械によって。これらの実装はあらゆる種類の逆参照をサポートすることはできません(実装は単純ではありませんが、キャプチャグループをサポートできます)。

私の考えでは、awk単純な時間とメモリの制限を一致させるために正規表現を使用する必要があります。

代わりに、perl / pcre / etcの "regexp"は、Turingシステムでのみ実装できる再帰的一致手順を説明する簡単な構文に進化しました。これはセキュリティに影響を与えます。信頼できないユーザーが正規表現を入力できる検索ボックスなどは、サービス拒否攻撃につながる可能性があり、そのような一致にどのくらいの時間やメモリが必要かを知ることはできません。厳格なランダム制限を実施し、持続的な豚を禁止するなどの措置を講じる必要があります。

これは古い記事これらすべてをより深く説明する著者Russ Cox。

答え4

残念ながら、POSIXや他の非busybox以外のawkでは、逆参照は正規表現でサポートされていないため、各行のすべての一意の文字を繰り返す必要があります。

$ cat tst.awk
{
    old = new = $0
    while (old != "") {
        char = substr(old,1,1)
        gsub(char,"",old)
        if ( char ~ /[[:alnum:]_]/ ) {
            gsub(char char,char char char,new)
        }
    }
    print new
}

$ echo ab aa cc de mn | awk -f tst.awk
ab aaa ccc de mn

上記の方法は、ターゲット文字が正規表現メタ文字ではない場合に機能します(この例のように)。 REメタ文字である可能性がある場合は、gsub()の正規表現コンテキストで使用する前にそれをエスケープする必要があります。必要に応じて、正規表現の代わりにchar"{2}"gsub()を使用できます。char char

busybox awkを使用してこれを行う方法の詳細については、@Stephaneの回答を参照してください。

関連情報