awk 的默认字段分隔符

Default field separator for awk

awk 的默认分隔符只有 space 吗?

让我们看一下 GNU awk 手册页:

FS — The input field separator, a space by default. See Fields, above.

字段部分!

As each input record is read, gawk splits the record into fields, using the value of the FS variable as the field separator. If FS is a single character, fields are separated by that character. If FS is the null string, then each individual character becomes a separate field. Otherwise, FS is expected to be a full regular expression. In the special case that FS is a single space, fields are separated by runs of spaces and/or tabs and/or newlines.

问题 the default delimiter is only space for awk? 含糊不清,但我会尽力回答您可能会问的两个问题。

FS 变量(保存字段分隔符,告诉 awk 在读取记录时如何将记录分隔成字段)的默认值是单个 space 字符。

awk 用来将记录分隔成字段的是一个 "field separator",它是一个具有一些附加功能的正则表达式,仅当字段分隔符是单个空白字符时才适用。该附加功能是:

  1. 前导和尾随白色 space 在字段拆分期间被忽略。
  2. 字段在连续 space 个字符链中分隔,其中包括空格、制表符和换行符。
  3. 如果您想使用文字空白字符作为字段分隔符,您必须将其指定为 [ ] 而不是像在正则表达式中那样只是一个独立的文字空白字符。

除了在读取输入时使用字段分隔符将记录拆分为字段外,它们还用于其他一些上下文,例如split() 的第三个参数,因此了解哪些上下文需要字符串、正则表达式或 fieldsep 很重要,手册页清楚地指定了每个。

除其他外,上面解释了这一点:

$ echo ' a b c ' | awk '{printf "%d: <%s> <%s> <%s>\n", NF, , , }'
3: <a> <b> <c>
$ echo ' a b c ' | awk -F' ' '{printf "%d: <%s> <%s> <%s>\n", NF, , , }'
3: <a> <b> <c>
$ echo ' a b c ' | awk -F'[ ]' '{printf "%d: <%s> <%s> <%s>\n", NF, , , }'                              
5: <> <a> <b>

所以如果你不明白为什么前两个产生相同的输出而最后一个不同,请问。

这是一个实用摘要,适用于所有主要的 Awk 实现

  • GNU Awk (gawk) - some Linux 发行版中的默认 awk
  • Mawk (mawk) - some Linux 发行版中的默认 awk (例如,Ubuntu crysman 的早期版本报告说版本 19.04 现在带有 GNU Awk - 请参阅下面的评论。 )
  • BSD Awk - a.k.a。 BWK Awk - 类 BSD 平台上的默认值 awk,包括 OSX

在 Linux 上,awk -W version 会告诉您默认的 awk 是哪个实现。
BSD Awk 理解awk --version(GNU Awk理解除了awk -W version)。

所有的最新版本这些实现遵循POSIX standard关于字段分隔符[1](但不是记录分隔符)。

词汇表:

  • RS输入-记录分隔符,它描述了如何将输入分成条记录:

    • POSIX-强制默认值是一个换行符,以下也称为\n;也就是说,输入默认被分成
    • awk的命令行中,RS可以指定为-v RS=<sep>
    • POSIX 将 RS 限制为 文字、单字符 值,但 GNU Awk 和 Mawk 支持 多字符 值可能是 扩展正则表达式 (BSD Awk 支持)。
  • FS输入-字段分隔符,它描述了如何将每条记录拆分为字段;它可能是一个 扩展正则表达式 .

    • awk的命令行中,FS可以指定为-F <sep>(或-v FS=<sep>)。
    • POSIX-mandated default value正式一个space(0x20 ),但 space 并非 字面上 解释为(唯一)分隔符,而是具有 特殊含义 ;见下文。

默认:

  • spaces中的任何运行and/or tabs and/or newlines 被视为 字段分隔符
  • 前导和尾随 运行被忽略。

POSIX 规范。 uses the abstraction <blank> for spaces and tabs,这适用于 所有 语言环境,但 可以 包含 其他 特定字符语言环境 - 我不知道是否存在这样的语言环境。

请注意,使用默认输入记录分隔符RS),\n换行 通常不要输入图片作为字段分隔符,因为没有记录本身中包含\n那种情况。

作为字段分隔符的换行符 do 发挥作用,但是:

  • RS 设置为导致记录 自身 包含 \n 个实例 的值(例如当 RS 设置为 空字符串 时;见下文)。
  • 通常,当split()函数用于将字符串拆分为没有显式字段分隔符参数的数组元素时。
    • 即使 输入记录 不包含 \n 个实例,以防默认 RS 生效,split() 函数当在来自不同源的多行字符串上调用时没有显式字段分隔符参数(例如,通过-v选项传递的变量或作为伪文件名) 始终\n 视为字段分隔符。

重要的非默认注意事项

  • 字符串赋给RS具有特殊意义:它读取段落模式,意思是输入被运行s of non-分成记录空行前导和尾随 运行 空行被忽略

  • 当你分配其他而不是文字space到FSFS的解读发生了根本性的变化:

    • 一个单个字符或来自指定字符的每个字符 单独 识别为字段分隔符 - 而不是默认的 运行s
      • 例如,将 FS 设置为 [ ] - 即使它 有效地 相当于一个 space - 导致每个 individual space实例中的每条记录都被当作字段分隔符。
      • 要识别运行s,必须使用正则表达式量词(重复符号)+;例如,[\t]+ 会将 运行s 个制表符识别为单个分隔符。
    • 前导和尾随分隔符不会被忽略,而是分隔字段.
    • FS设置为空字符串意味着每个字符 一条记录是 它自己的字段.
  • mandated by POSIX一样,如果RS设置为空字符串(段落模式),newlines (\n) 被认为是字段分隔符,与 [= 的值无关25=].

[1] 不幸的是,GNU Awk 至少版本 4.1.3 符合关于字段分隔符的 obsolete POSIX 标准当您使用该选项强制执行 POSIX 合规性时,-P (--posix):该选项生效且 RS 设置为 非空 值,换行符(\n 实例)不被识别为字段分隔符。 GNU Awk 手册阐明了过时的行为(但忽略了当 RS 设置为 empty 字符串时它不适用)。 POSIX 标准在 2008 年更改为 alsoFS 具有其默认值时考虑 newlines 字段分隔符- 正如 GNU Awk 一直做的那样 without -P (--posix).
以下是验证上述行为的 2 个命令:
* -P 生效且 RS 设置为 空字符串 \n still 被视为字段分隔符:
gawk -P -F' ' -v RS='' '{ printf "<%s>, <%s>\n", , }' <<< $'a\nb'
* 在 -P 生效且 非空 RS 时,\n 不被视为字段分隔符 - 这是过时的行为:
gawk -P -F' ' -v RS='|' '{ printf "<%s>, <%s>\n", , }' <<< $'a\nb'
根据 GNU Awk 维护者的说法,即将修复;期待版本 4.2(未给出时间范围)。
(感谢@JohnKugelman 和@EdMorton 的帮助。)