实时更改 AWK 的字段分隔符

5
我希望使用AWK来处理以下电子表格,其中名和姓在同一列中:
Peter Griffin, 31 Spooner St, Quahog
Homer Simpson, 732 Evergreen Terr, Springfield
Fred Flintstone, 301 Cobblestone Way, Bedrock

并将输出到一个新的电子表格中,其中名字和姓氏有自己的列:

Peter, Griffin, 31 Spooner St, Quahog
Homer, Simpson, 732 Evergreen Terr, Springfield
Fred, Flintstone, 301 Cobblestone Way, Bedrock

我尝试过在运行时更改字段分隔符,类似于这样的操作:
awk '{print $1 "," $2} {FS=","} {print $3} {FS=" "}' spreadsheet.csv

但是它似乎不能正常工作,我得到了一堆混乱的东西。使用AWK可以实现这个吗?


1
全名是否总是由两个单词组成? - Tom Fenech
2
FYI,逗号是一个糟糕的字段分隔符,用于姓名/地址电子表格,因为它们通常在姓名和地址中使用。你应该使用制表符代替。 - Ed Morton
2
@oguzismail 因为 OP 在记录已经被拆分成字段之后将 FS 设置为“,”,然后在拆分下一个记录之前将其设置回“ ”。 - Ed Morton
1
@Ed 噢,对了,有没有办法强制 awk 在更改 FS 后重新拆分字段? - oguz ismail
2
@oguzismail 是的 - $0=$0 - Ed Morton
显示剩余2条评论
4个回答

2

将第一个空格替换为逗号和空格:

$ sed 's/ /, /' file.csv
Peter, Griffin, 31 Spooner St, Quahog
Homer, Simpson, 732 Evergreen Terr, Springfield
Fred, Flintstone, 301 Cobblestone Way, Bedrock

在这里,s/ /, / 是一个替换命令。它将找到的第一个 替换为 ,

要直接更改文件,请使用 -i 选项:

sed -i.bak 's/ /, /' file.csv

原地选项是GNU扩展,不适用于Unix/Solaris系统。 - Daniel Liston
@DanielListon,你说得对,solaris sed没有-i选项。虽然POSIX标准并不要求使用-i选项,但它在GNU sed、MacOS sed和FreeBSD sed中都是可用的。OP将此问题标记为Linux。 - John1024
明白了。然而,OP请求并标记了一个AWK解决方案,而不是sed。如果提供了另一种解决方案,应清楚地表明GNU提供的同名实用程序和它们的原始程序版本之间存在差异。特别是如果在示例中提供的修饰符不能在所有unix/linux平台上工作。 - Daniel Liston

2
另一种可能性。
awk '{$1=$1","}1' file

Peter, Griffin, 31 Spooner St, Quahog
Homer, Simpson, 732 Evergreen Terr, Springfield
Fred, Flintstone, 301 Cobblestone Way, Bedrock

} 后面的数字1是什么意思?我尝试了其他数字,结果相同。我在手册中找不到答案,甚至阅读了awk源代码也无法猜测。 - Et7f3XIV

2
只需在第一个以,为基础的字段中发现空格时添加逗号:
awk 'BEGIN {FS=OFS=","} {sub(/ /, ", ", $1)}1' file
#                             ^    ^^
#               find a space...    ... replace it with , plus space

使用您的文件:

$ awk 'BEGIN {FS=OFS=","} {sub(/ /, ", ", $1)}1' file
Peter, Griffin, 31 Spooner St, Quahog
Homer, Simpson, 732 Evergreen Terr, Springfield
Fred, Flintstone, 301 Cobblestone Way, Bedrock

这里使用了函数sub()来替换第一个字段中的内容。


不是“每当有空格”而是“第一个空格”的时候,否则“Harry Connick Jr”不能在你的数据库中 :-)。你的代码是正确的,只是你的文本有误。 - Ed Morton
@EdMorton 嗯,是的,尽管考虑到你们美国人在名字中有很多senior、jr和III,也许只对$1使用gsub()可能更好;D - fedorqui
@fedorqui 这就是我在寻找的,谢谢!在右花括号闭合后面加上1的目的是什么?我注意到我可以用任何数字代替1,命令仍然有效。 - Ryan R
@RyanR 是的,这是因为 15 或任何数字都会被评估为 True 并触发 awk 的默认操作,即打印当前行。因此,1 等同于 {print $0} - fedorqui
“你们美国人”?我不是美国人。我是苏格兰人。无论如何,如果你添加更多逗号,它会破坏OP文件的(不明智的)逗号分隔布局。 - Ed Morton
@fedorqui,我屏幕上仍然可以看到你的评论,不知道为什么你看不到它。我并没有觉得它有冒犯之处,只是不准确而已 :-)。所以我肯定没有标记它。是的,楼主想要从那个字段中创建确切的两个字段。 - Ed Morton

1
你可以使用多个分隔符,例如“-”。
awk -F '[ ,]' '{print $1 ", " $2 ", " $3 $4 " " $5 " " $6 ", " $7 " " $8}' file

输出-

Peter, Griffin,  31 Spooner St,  Quahog
Homer, Simpson,  732 Evergreen Terr,  Springfield
Fred, Flintstone,  301 Cobblestone Way,  Bedrock

你需要跟踪定义的“列”。

不要这样做。这是不必要的硬编码,当地址是其他格式时会失败,例如 19 Martin Luther King Dr 或者名字是 Harry Connick Jr - Ed Morton
是的,这变成了一个具体的解决方案。完全不可移植。但它解决了在同一awk行中如何使用多个分隔符的问题。 - Chem-man17
1
但是使用多个分隔符并不是解决这个问题的正确方法,因为它会带来更多问题而不是解决问题。如果确实需要使用多个分隔符,应该写成-F '[ ,]'而不是-F ' |,'(考虑一下如果有10个分隔符字符的区别)。 - Ed Morton
1
同意。修改以反映更好的方式。 - Chem-man17

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接