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간단한 시간 및 메모리 제한 일치를 위해서는 정규식을 사용해야 하고, 그보다 더 복잡한 것을 처리하려면 C와 유사한 Turing-complete 언어를 사용해야 합니다.

대신, 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의 답변을 참조하세요.

관련 정보