![为什么 awk -F 对大多数字母有效,但对字母“t”无效?](https://linux22.com/image/207907/%E4%B8%BA%E4%BB%80%E4%B9%88%20awk%20-F%20%E5%AF%B9%E5%A4%A7%E5%A4%9A%E6%95%B0%E5%AD%97%E6%AF%8D%E6%9C%89%E6%95%88%EF%BC%8C%E4%BD%86%E5%AF%B9%E5%AD%97%E6%AF%8D%E2%80%9Ct%E2%80%9D%E6%97%A0%E6%95%88%EF%BC%9F.png)
July 2022 mac os Monterey V12.1
awk --version 20200816
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin21)
为什么awk -F
适用于大多数字母,但不适用于字母t
?我有解决方案,但我想了解为什么 awk 对于 letter 失败t
。
# Count 'e's
% echo "tweeter" | awk -F "e" '{print NF-1}'
3
# Count 'r's
% echo "tweeter" | awk -F "r" '{print NF-1}'
1
# (Attempt to) count 't's
% echo "tweeter" | awk -F "t" '{print NF-1}'
0 <=== ????
# Use gsub()
% echo "tweeter" | awk '{print gsub(/t/, "")}'
2
答案1
因为:
通常,任意数量的空格分隔字段。要将字段分隔符设置为单个空格,请使用值为 的 -F 选项
[ ]
。 如果指定了字段分隔符t
,awk 会将其视为\t
已指定并使用 <TAB> 作为字段分隔符。 为了使用文字t
作为字段分隔符,请使用值为 的 -F 选项[t]
。
那是来自FreeBSD awk 手册页,macOS 附带的实用程序通常是一些旧的 FreeBSD 版本等。
$ printf 'foo\tbar\n' | awk -F t '{print NF-1}'
1
$ echo total | awk -F '[t]' '{print NF-1}'
2
在某种程度上,这对于具有制表符分隔值的文件来说似乎是一个有用的速记,但是对于按原样采用的其他字母,它会令人困惑。它只能像 with 那样工作-F
,而 using 则-v FS=t
不起作用。
该功能是非 POSIX 的,因为POSIX 说这-F x
与 相同-v FS=x
。我测试的大多数其他 awks 都被t
视为字面字母(某些版本的 gawk、mawk 和 Busybox)。
例如 Debian 中的 awk 版本original-awk
包裹(“One True AWK”或“BWK awk”大概来自 Brian W. Kernighan 的姓名缩写)确实支持它,并且至少维基百科似乎表明这与 FreeBSD 使用的软件相同。这个版本似乎是基于 1988 年的《The AWK 编程语言》一书中描述的版本,但我不是 awk 谱系的专家,也不知道从那时起它是否已经发生了显着的演变。那个在 github 上,但那里的文档似乎没有描述该功能。这特殊情况可以看代码(在评论中将其描述为“疣”)。
不过,您可以在 BWK-awk 兼容模式下使用 GNU awk 获得相同的行为。:
作为一种特殊情况,在兼容模式下(请参阅命令行选项部分),如果 -F 的参数为“t”,则 FS 将设置为 TAB 字符。如果您在 shell 中键入“-F\t”,不带任何引号,则“\”将被删除,因此 awk 会认为您确实希望用制表符而不是“t”分隔字段。
答案2
这绝对地无论 shell/env 组合如何,最安全的方法是使用方笼和双反斜杠作为选项卡
FS='[t]' # for just lowercase "t"
FS='[\\411]' # for the tab "\t"
即使\\
它在单引号内,即使它在单引号内,如果任何环境变得太聪明并尝试剥离其中一个,awk
它仍然会作为制表符本身的底层字节。
如果你是真的对此感到偏执,然后做
FS='[\534\564]' # for the tab "\t"