如果字段分隔符为空字符串,则每个字符将成为单独的字段
$ echo hello | awk -F '' -v OFS=, '{$1 = NF OFS $1} 1'
5,h,e,l,l,o
但是,如果FS是一个可能匹配零次的正则表达式,那么不会发生相同的行为:
$ echo hello | awk -F ' *' -v OFS=, '{$1 = NF OFS $1} 1'
1,hello
任何人都知道为什么会这样吗?我在gawk manual找不到任何内容。 FS=""
只是一个特例吗?
我最感兴趣的是理解为什么第二种情况不会将记录分成更多字段。好像awk正在处理FS=" *"
1}} FS=" +"
答案 0 :(得分:4)
有趣的问题!
我刚刚提取了gnu-awk 4.1.0的代码,我想我们可以在field.c
文件中找到答案。
line 371:
* re_parse_field --- parse fields using a regexp.
*
* This is called both from get_field() and from do_split()
* via (*parse_field)(). This variation is for when FS is a regular
* expression -- either user-defined or because RS=="" and FS==" "
*/
static long
re_parse_field(lo...
也是这一行:(line 425
):
if (REEND(rp, scan) == RESTART(rp, scan)) { /* null match */
这是您问题中<space>*
匹配的情况。实现没有增加nf
,也就是说,它认为整行是一个单独的字段。请注意,此功能也用于do_split()
功能。
首先,如果FS
为空字符串,则gawk将每个字符分隔为其自己的字段。 gawk的文档清楚地写了这个,也在代码中,我们可以看到:
line 613:
* null_parse_field --- each character is a separate field
*
* This is called both from get_field() and from do_split()
* via (*parse_field)(). This variation is for when FS is the null string.
*/
static long
null_parse_field(long up_to,
如果FS
有单个字符,awk不会将其视为正则表达式。这也在doc中提到过。也在代码中:
#line 667
* sc_parse_field --- single character field separator
*
* This is called both from get_field() and from do_split()
* via (*parse_field)(). This variation is for when FS is a single character
* other than space.
*/
static long
sc_parse_field(l
如果我们读取该函数,那里没有进行正则表达式匹配处理。
在函数re_parse_field()
和sc_parse_field()
的评论中,我们看到do_split
也会调用它们。它解释了为什么我们在以下命令中使用1
而不是3
:
kent$ echo "foo"|awk '{split($0,a,/ */);print length(a)}'
1
注意,为避免帖子过长,我没有在这里粘贴完整的代码,我们可以在这里找到代码:
答案 1 :(得分:2)
如前所述,空字段分隔符会生成未定义的行为;相同的代码将在awk
的不同平台/风格上给出不同的结果。例如(所有Mac OSX 10.8.5):
> echo hello | awk -F '' -v OFS=, '{$1 = NF OFS $1} 1'
awk: field separator FS is empty
1,hello
所以awk
抱怨,但继续前进。
让我们看看其他一些例子:
> echo hello | awk -F '.' -v OFS=, '{$1 = NF OFS $1} 1'
1,hello
.
本身不被视为正则表达式
> echo hello | awk -F '[.]' -v OFS=, '{$1 = NF OFS $1} 1'
1,hello
仍然没有
> echo hello | awk -F '.?' -v OFS=, '{$1 = NF OFS $1} 1'
6,,,,,,
现在我们有类似正则表达式的东西:.?
是“零或一个字符”。它被扩展为一个字符(被消耗),因此输出是“很多无用的”
> echo hello | awk -F '*' -v OFS=, '{$1 = NF OFS $1} 1'
1,hello
不是正则表达式
> echo hello | awk -F '.*' -v OFS=, '{$1 = NF OFS $1} 1'
2,,
使用整个事物的正则表达式
> echo hello | awk -F 'l' -v OFS=, '{$1 = NF OFS $1} 1'
3,he,,o
匹配字母l
两次 - 两个空字符串
> echo hello | awk -F 'ell' -v OFS=, '{$1 = NF OFS $1} 1'
2,h,o
一次匹配所有ell
> echo hello | awk -F '.?|' -v OFS=, '{$1 = NF OFS $1} 1'
awk: illegal primary in regular expression .?| at
input record number 1, file
source line number 1
尝试聪明:有时一边有空字符串的|
会匹配“任何”,但awk
的正则表达式引擎不喜欢它。
结论 - 正则表达式不能匹配“空”,并且消耗了匹配的任何内容。尝试使用(?:.)
甚至(?=.)
会产生错误。
答案 2 :(得分:1)
传统上,FS的行为等于“”未定义。在这 大多数版本的Unix awk只是将整个记录视为唯一 有一个领域。 (d.c.)在兼容模式下(参见选项),如果是FS 空字符串,然后gawk也表现这种方式。
答案 3 :(得分:1)
答案 4 :(得分:0)
另一个数据点:gawk和perl不同意如何做到这一点:
$ perl -E '$,=","; $s="hello"; $r=qr( *); @s=split($r,$s); say scalar(@s), @s'
5,h,e,l,l,o
$ gawk 'BEGIN {s="hello";r=" *";n=split(s,a,r); print n,a[n]; if (s~r) print "match"}'
1 hello
match
$ gawk 'BEGIN {s="hello";r=""; n=split(s,a,r); print n,a[n]; if (s~r) print "match"}'
5 o
match