glibc regex(7)/GNU sed/grep/egrep 정규 표현식에서 이스케이프 횟수를 계산하는 가장 좋은 방법은 무엇입니까?

glibc regex(7)/GNU sed/grep/egrep 정규 표현식에서 이스케이프 횟수를 계산하는 가장 좋은 방법은 무엇입니까?

주어진 bash 환경 변수 설정:

 $ declare -g bs=$'\\' bsbs=$'\\\\' q="'";

이 정규식은 작은따옴표("'") 텍스트의 시퀀스와 정확하게 일치합니다. 여기서 이러한 텍스트에는 이스케이프된 작은따옴표가 포함될 수 있습니다.

 "[${bs}${q}]((([^${bsbs}]?[^${bs}${q}])|(${bsbs}${bs}${q}))+)[${bs}${q}]"

 $ echo "[${bs}${q}]((([^${bsbs}]?[^${bs}${q}])|(${bsbs}${bs}${q}))+)[${bs}${q}]"
 [\']((([^\\]?[^\'])|(\\\'))+)[\']

("[\']"의 역따옴표는 꼭 필요한 것은 아니지만 누군가가 이 값을 작은따옴표로 묶은 문자열로 인코딩하려고 시도하는 경우 명확성을 위해 포함됩니다.)

문제는 이것을 이스케이프 따옴표 문자로 일반화하는 최선의 방법과 입력 이스케이프 문자가 홀수 길이((n&1)==1) 크기(바이트 수)인 경우에만 여러 이스케이프 시퀀스의 실행을 처리하는 방법입니다. 마지막 이스케이프는 ACTIVE이고 마지막 문자는 INACTIVE(문자열의 일부)입니다. 그렇지 않으면(이스케이프 횟수는 짝수((n&1)==0)), 문자열에는 이스케이프 횟수의 절반이 포함됩니다(n> >1). 마지막 문자는 ACTIVE(즉, 이스케이프되지 않음)입니다.

또한 sed 및 grep/egrep에서는 몇 가지 문제가 있습니다.

o 일치하는 하위 그룹은 후속 "\1+" 그룹 번호를 차지하여 숫자를 늘릴 수 있습니다. - 후속 그룹이 일치하지 않는 경우 -

  • 이상적으로는 후속 하위 그룹 번호에 영향을 미칠 수 있는 하위 그룹 없이 이 정규식을 표현할 수 있기를 바랍니다.

o 이스케이프 번호를 전혀 처리하지 않으며,
이스케이프 번호에 의한 참조도 이스케이프되지 않는다는 것을 인식하지 못합니다.

그래서 내 질문은 다음과 같습니다

glibc 지원 POSIX RE 또는 grep/sed RE만을 사용하여 이러한 문제를 해결하는 가장 좋은 방법은 무엇입니까?

즉. RegExp 내에서 임의 길이의 홀수(유효한 이스케이프) 또는 짝수(잘못된 이스케이프) 길이의 이스케이프 시퀀스가 ​​인식되도록 허용하시겠습니까?

나는 POSIX RE가 이런 종류의 문제를 처리하기 위해 다음과 같은 특수 구문의 이점을 누릴 수 있다고 생각합니다.

 [\\]{1,}\#&1\?$A\:$B

여기서 '}#&1'은 이전 [\]{...} 그룹 'x & 1'과 일치하는 요소 수에 대한 테스트를 의미하고, ?x:y는 "마지막 테스트가 true이면 x를 바꾸고, 그렇지 않으면 x를 대체합니다"를 의미합니다. RE의 y”.

그러면 실제로 이를 RegExp 구문 분석 문자열의 모든 이스케이프 수에 관계없이 쉽고 안전하게 처리할 수 있습니다. 이와 같은 새로운 RE 구문 없이 이를 수행하는 방법은 무엇입니까?

RegExp 표현식만 사용하는 것은 불가능하거나 실행 불가능하지는 않지만 매우 어렵습니다.

아니면 내가 틀렸나요?

이제 최신 POSIX RE에서 이전 세트의 실행 길이에 대해 산술을 수행하는 쉬운 방법이 있습니까?

예시 1:

$ declare -g bs=$'\\' bsbs=$'\\\\' q="'";

$ echo "'a quot\\'d string' 42" | sed -r 's/'"[${bs}${q}]((([^${bsbs}]?[^${bs}${q}])|(${bsbs}${bs}${q}))+)[${bs}${q}]"'[[:space:]]([0-9]+)/\1\t:\t\2/'
'a quot'd string    :   g

예 2:

$ echo "'a quot\\'d string' 42" | 
  sed -r 's/'"[${q}]((([^${bsbs}]?[^${q}])|(${bsbs}${q}))+)[${q}]"'[[:space:]]([0-9]+)/\1\t:\t\2/'
a quot\'d string    :   g

@rowboat가 언급한 ${bs}-es가 어떻게 제거되었는지 확인하세요. 결과는 $bsbs 대신 $bs를 사용한 것과 여전히 동일합니다.

$ echo "'a quot\\'d string' 42" | sed -r 's/'"[${q}]((([^${bs}]?[^${q}])|(${bs}${q}))+)[${q}]"'[[:space:]]([0-9]+)/\1\t:\t\2/'
a quot\'d string    :   g

결론적으로:

저는 glibc, PCRE, PERL, cl-ppcre(SBCL의 Common Lisp RE 라이브러리) 및 Emacs의 RE 라이브러리에서 제공하는 "regex(7) - POSIX.2 정규 ​​표현식" 라이브러리에 대한 비 POSIX 확장을 개발 중입니다.

o 이름이 지정된 POSIX 문자 클래스(예: '[[:spaceesc:]]' 또는 '[^[:space-esc:]]' 또는 '[[)에 대해 접미사 "-esc" 또는 "esc"의 의미를 정의합니다. : quote-esc:]]' 이는 일반적으로 문자 클래스 'X'의 멤버인 문자가 문자 클래스 '${X}esc'('${X}-의 동의어)의 멤버가 아님을 의미합니다. esc') IFF 앞에 나옵니다. 홀수 개의 이스케이프 문자('\':ASCII "\x5c" )가 있습니다.

 All character sequences that are subject to an :*esc: character
 class test will have legal '\\' , '\xXX', '\0OOO', or '\Uxxxxxx' or
'\uXXXX' sequences replaced by :
 ASCII:\x5c , ASCII:\xXX (where XX are hex digits), 
 ASCII:\OOO (where OOO are Octal digits) ,
 24-bit unicode value with code point xxxxxx (x: hex digit) , and
 16-bit unicode value with code point xxxx (x: hex digit) ,
 respectively.

 Also '[[:quote:]]' and '[[:quoteesc:]]' classes must be
 supported that select characters (or non-escaped chars)
 with the Unicode 'Quotation Mark' binary attribute, and
 '[[:punct:]]' or '[[:punctesc:]]' would similarly apply
 to all (non-escaped) chars which have the Punctuation attribute.

 Perhaps a similar '*cesc' or '*escc' character class suffixes
 could be provided that support also the C escapes:
  '\n','\r','\t','\v','\b','\l'... etc.

 If the /

답변1

요점이 쉘 언어 해석기처럼 쉘 코드를 마크업하는 것이라면 정규 표현식은 도움이 되지 않습니다.

zsh 셸은 z매개변수 확장 플래그를 사용하여 토크나이저를 노출합니다(또는 Z주석을 처리하거나 개행 처리를 변경하는 옵션을 사용할 수 있음). 이를 Q매개변수 확장과 결합하여 따옴표를 제거할 수 있습니다.

예를 들어:

tokens() printf ' - « %s »\n' ${(Z[Cn])1}
tokens_dequoted() printf ' - « %s »\n' "${(@Q)${(Z[Cn])1}}"

첫 번째 인수의 모든 쉘 토큰을 보고하고 두 번째 인수에서도 인용의 한 레이어를 제거합니다.

$ tokens '  foo "a b"; "" "$(echo "x y")" <<'"'qwe '\''qwe' #qwe"
 - « foo »
 - « "a b" »
 - « ; »
 - « "" »
 - « "$(echo "x y")" »
 - « << »
 - « 'qwe '\''qwe' »
$ tokens_dequoted '  foo "a b"; "" "$(echo "x y")" <<'"'qwe '\''qwe' #qwe"
 - « foo »
 - « a b »
 - « ; »
 - «  »
 - « $(echo "x y") »
 - « << »
 - « qwe 'qwe »

동일한 작업을 수행하려면 완전한 쉘 파서를 구현해야 함을 알 수 있습니다.

범위를 좁히면 정규 표현식을 사용하여 다음과 같은 결과를 얻을 수 있습니다. , 따옴표 유형( 대신 ) ​​만 고려 '...'하고 "..."공백 \$'...'구분 기호로 간주하고 큰따옴표 안의 확장은 무시합니다. bash 4.4+에서는 어쨌든 코드에서 NUL 바이트를 처리할 수 없는 zsh와 달리 GNU를 사용하면 grep다음을 수행할 수 있습니다.

tokens() {
  local tokens
  readarray -td '' tokens < <(printf %s "$1" |
    grep -Ezo '(\\.|[^[:space:]\\"'\'']|'\''[^'\'']*'\''|"(\\.|[^\\"])*")+'
  )
  printf ' - « %s »\n' "${tokens[@]}"
}

그 다음에:

$ tokens ' foo "a b"\c\\\" c\ d '" 'qwe'\''qwe'\"'\"qwe"
 - « foo »
 - « "a b"\c\\\" »
 - « c\ d »
 - « 'qwe'\''qwe'"'"qwe »

참조 레이어를 제거하려면 다음을 사용합니다 perl(또는 zsh위에 표시된 대로 즉시 수행할 수도 있음).

답변2

더 나은 답변: pcre/PERL RegExps를 사용하십시오.

$ cat a.pcre
/^[']((?|(?:[^\\]?[^'\t\n\r])|(?:[\\]['\t\n\r]))*)[']\t((?|(?:[^\\]?[^\t])|(?:[\\][^\t\n\r]))+)/
'A quot\'d\ tab containing string'  42

$ pcretest < a.pcre 
PCRE version 8.45 2021-06-15

re> data>  0: 'A quot'd\x09tab containing string'\x0942
1: A quot'd\x09tab containing string
2: 42
data> 

관련 정보