【问题标题】:Change AWK field separator on the fly即时更改 AWK 字段分隔符
【发布时间】:2016-08-30 15:37:35
【问题描述】:

我想使用 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 吗?

【问题讨论】:

  • 全名是否总是只包含两个单词?
  • 仅供参考,逗号对于名称/地址电子表格来说是一个糟糕的字段分隔符,因为它们通常用于名称和地址中。您应该改用标签。
  • 嘿@Ed,你知道为什么更新FS在这里不起作用吗?我在 POSIX 规范中找不到解释
  • @oguzismail 因为 OP 将 FS 设置为 , 记录已被拆分为字段,然后在拆分之前再次将其设置回 " "下一条记录。
  • @oguzismail 是 - $0=$0.

标签: linux bash awk


【解决方案1】:

只要在第一个基于 , 的字段中找到空格,只需添加一个逗号:

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()在第一个字段中执行替换。

【讨论】:

  • 不是whenever a space 而是when the first space 否则Harry Connick Jr 不能在您的数据库中:-)。您的代码是正确的,只是您的文字有误。
  • @EdMorton 嗯,是的,尽管考虑一下gsub() 可能是最好的,因为你们美国人的名字中有很多高级,jr 和 III ;D跨度>
  • @fedorqui 这就是我要找的,谢谢!右花括号后的 1 的目的是什么?我注意到我可以用任何数字代替 1,并且该命令仍然有效。
  • @RyanR 是的,这是因为 15 或任何数字的计算结果为 True 并触发 awk 的默认操作,包括打印当前行。所以1 等价于{print $0}
  • you Americans?我不是美国人。我是苏格兰人。无论如何,如果您添加更多逗号,它将破坏 OPs 文件的(不明智的!)逗号分隔的布局。
【解决方案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

【讨论】:

  • in-place 选项是 GNU 扩展,在 unix/solaris 中不起作用。
  • @DanielListon 你说得对solaris sed not having a -i option。 POSIX 不需要 -`i 选项,但它在 GNU sed、MacOS sed 和 FreeBSD sed 上可用。 OP 为 Linux 标记了这个问题。
  • 明白。但是,OP 请求并标记了 AWK 解决方案,而不是 sed。如果提供了替代解决方案,应该清楚 GNU 提供的同名实用程序与其原始程序版本之间存在差异。特别是如果示例中提供的修饰符不适用于所有 unix/linux 平台。
【解决方案3】:

另一种可能性。

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 源代码但无法猜测的人中找不到。
【解决方案4】:

您可以使用多个分隔符作为-

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
  • 是的,这变成了一个特定的解决方案。根本不便携。但它解决了如何在同一 awk 行中使用多个分隔符的问题。
  • 但是使用多个分隔符是解决这个问题的错误方法,因为它增加的问题多于解决的问题,如果有必要,您可以将其写为-F '[ ,]',而不是-F ' |,'(考虑差异如果你有 10 个分隔符)。
  • 承认。编辑以反映更好的方式。
猜你喜欢
  • 1970-01-01
  • 2017-11-14
  • 1970-01-01
  • 1970-01-01
  • 2020-11-24
  • 2020-12-02
  • 1970-01-01
  • 2017-06-14
  • 1970-01-01
相关资源
最近更新 更多