了解 awk 分隔符 - 在基于正则表达式的字段分隔符中转义

Kor*_*Jek 2 awk escaping delimiter

我有以下 shell 命令:

awk -F'\[|\]' '{print $2}'
Run Code Online (Sandbox Code Playgroud)

这个命令是做什么的?使用作为分隔符分割成字段[sometext]

例如:

$ echo "this [line] passed to awk" | awk -F'\[|\]' '{print $2}'
line
Run Code Online (Sandbox Code Playgroud)

编者注:只有Ubuntu 上默认使用的Mawk才会生成上述输出。

mkl*_*nt0 5

明显意图是将文字 []视为字段分隔符[,即通过每次出现和/或,将每个输入记录拆分为字段],这与示例行一起生成this\xc2\xa0字段 1 ( $1)line和字段 2 ( $2) ,并且\xc2\xa0passed to awk作为最后一个字段($3 )。

\n\n

这是通过使用交替( ) 的正则表达式(正则表达式)来实现的,其任一侧定义字段分隔符(分隔符):在正则表达式中需要和来表示文字和,因为默认情况下和是所谓的元字符(具有特殊语法意义的字符)。\n请注意,始终将变量(选项)的值解释为正则表达式|\\[\\] [][]
awk FS-F

\n\n

然而,正确的形式是\'\\\\[|\\\\]\'

\n\n
$ echo "this [line] passed to awk" | awk -F\'\\\\[|\\\\]\' \'{print $2}\'\nline\n
Run Code Online (Sandbox Code Playgroud)\n\n

也就是说,使用字符集 ( [...]) 而不是交替 ( |) 的更简洁版本是:

\n\n
$ echo "this [line] passed to awk" | awk -F\'[][]\' \'{print $2}\'\nline\n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意,为了使这项工作有效,]before[在封装内的仔细放置,以及封装现在如何具有特殊含义:它们封装了一字符,其中任何一个都匹配。[...] [...]

\n\n
\n\n

至于为什么需要2个 \\\'\\\\[|\\\\]\'实例:

\n\n

作为孤立的正则表达式,\\[|\\]可以工作:

\n\n
    \n
  • \\[匹配字面意思[
  • \n
  • \\]匹配字面意思]
  • \n
  • |是与其中一个或另一个相匹配的交替。
  • \n
\n\n

然而,Awk 的字符串处理是第一位的

\n\n
    \n
  • 由于在字符串中进行处理,它应该减少到解释为正则表达式之前。\\\\[|\\][|]

    \n\n
      \n
    • 然而不幸的是,例如,Mawk(Ubuntu 上的默认 Awk)在这种特定情况下只能依靠猜测[1]
    • \n
  • \n
  • [|],解释为正则表达式,然后将仅匹配单个文字 |

  • \n
\n\n

因此,当您打算将单个值作为regex的一部分传递时,健壮且可移植的方法是字符串文字中使用。\\\\ \\

\n\n

此引用来自GNU Awk 手册的相关部分总结得很好:

\n\n
\n

要将反斜杠添加到字符串内的正则表达式中,您必须键入两个反斜杠。

\n
\n\n
\n\n

[1]实现差异:

\n\n

不幸的是,至少有 1 个主要的 Awk 实现在存在单个\\在字符串文字中的正则表达式元字符之前

\n\n

\\BSD/macOS Awk 和 GNU Awk 的行为符合预期,并且当发现单前缀正则表达式元字符时,GNU Awk 还会发出有用的警告:

\n\n
# GNU Awk: Predictable string-first processing + a helpful warning.\necho \'a[b]|c\' | gawk -F\'\\[|\\]\' \'{print $2}\'\ngawk: warning: escape sequence \'\\[\' treated as plain \'[\'\ngawk: warning: escape sequence \'\\]\' treated as plain \']\'\nc\n\n# BSD/macOS Awk: Predictable string-first processing, no warning.\necho \'a[b]|c\' | awk -F\'\\[|\\]\' \'{print $2}\'\nc\n\n# Mawk: *Guesses* that a *regex* was intended.\n#       The unambiguous form -F\'\\\\[|\\\\]\' works too, fortunately.\necho \'a[b]|c\' | mawk -F\'\\[|\\]\' \'{print $2}\'\nb\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

可选阅读:Awk 脚本中的正则表达式文字

\n\n

awk 支持包含在 中的正则/.../表达式文字,使用它可以绕过双重转义问题。

\n\n

然而:

\n\n
    \n
  • 这些文字(始终是常量)仅在Awk 脚本可用,
  • \n
  • 而且,看来您只能将它们用作模式函数参数- 不能将它们存储在变量中。
  • \n
\n\n

因此,即使原则上相当于/\\[|\\]/,您也不能使用以下内容,因为正则表达式文字不能分配给(特殊)变量:"\\\\[|\\\\]"FS

\n\n
# !! DOES NOT WORK in any of the 3 major Awk implementations.\n#    Note that nothing is output, and no error/warning is displayed.\n$ echo \'a[b]|c\' | awk \'BEGIN { FS=/\\[|\\]/ } { print $2 }\'\n\n# Using a double-escaped *string* to house the regex again works as expected:\n$ echo \'a[b]|c\' | awk \'BEGIN { FS="\\\\[|\\\\]" } { print $2 }\'\nb\n
Run Code Online (Sandbox Code Playgroud)\n