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}}"

最初の引数のすべてのシェルトークンを報告し、2番目の引数から引用符の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> 

関連情報