【问题标题】:awk ignore quoted delimiters `echo 'a "b1 b2" c'| awk '{print $2}'`awk 忽略带引号的分隔符 `echo 'a "b1 b2" c'| awk '{打印 $2}'`
【发布时间】:2019-10-17 08:36:08
【问题描述】:

以下将输出"b1,因为它将引用的空格识别为字段分隔符。如何告诉 awk 忽略带引号的分隔符,以便输出 b1 b2"b1 b2"

echo 'a "b1 b2" c'| awk '{print $2}'

我看到以下两个相关的帖子,但我无法让解决方案发挥作用。我希望找到一个简单的解决方案。字段解析是 awk 的专长吧?

awk ignore delimiter inside single quote within a parenthesis What's the most robust way to efficiently parse CSV using awk?

【问题讨论】:

  • awk '{print $2}' FS=\"
  • 您真的在尝试解析 CSV 文件吗?

标签: awk


【解决方案1】:

使用gawk (GNU awk),您可以使用FPAT 特殊变量来定义字段的外观,而不是受限于指定分隔符:

echo 'a "b1 b2" c'| gawk '{print $2}' FPAT='("[^"]+")|[^[:blank:]]+'

这里我们说:一个字段要么是 " 后跟非 " 字符和结束 " -> ("[^"]+") ... 或 | 一系列非空白字符 -> [^[:blank:]]+

这些正则表达式将按顺序计算,因此 "" 中包含的字段优先于第二个模式,即非空白字符序列(awk 的默认值)。


参见 GNU awk 手册:Defining fields by content

【讨论】:

  • 不幸的是,包含双引号的字段(例如 'a "b1""b2" c' ,其中 $2="b1""b2")会破坏这一点,但这是 gawk 的一个不错的功能,我从来没有用过……
  • 是的,很好。只要数据至少遵循 some 标准,您就可以更改模式。如果数据可以是任何东西,则无法解析它。
  • 请修复您的 FPAT,以在以下命令 echo 'a "b1 b2" "c1 c2 c3" d "e1 e2" ftext e g1' |awk '{for(i=1;i<NF;i++)print $i}' FPAT='("[^"]+")|[^[:blank:]]+' 中包含最后一个字段 g1,因为它工作得很好,但最后一个字段
  • 在你的例子中应该是for(i=1;i<=NF;i++)。注意:<= 而不是<
【解决方案2】:

awk 没有我想要的对引用字段的简单、方便的支持。我也看了 cut ,但也没有。

另一个广泛使用的 bash shell 工具 csvcut 包含在称为 csvkit 的工具包中,它确实为引用字段提供了简单的支持。我的数据是空格分隔的,不是逗号分隔的,但我可以很容易地为 csvcut 工具指定一个空格分隔符。

这就是我想要的:

# Gives a
echo 'a "b1 b2" c d e' | csvcut -d ' ' -c 1
# Gives b1 b2
echo 'a "b1 b2" c d e' | csvcut -d ' ' -c 2
# Gives c
echo 'a "b1 b2" c d e' | csvcut -d ' ' -c 3

【讨论】:

    【解决方案3】:

    最短答案:

    echo 'a "b1 b2" c'| awk -F\" '{print $2}'
    

    将输出:b1 b2

    【讨论】:

    • 完美!这样可行!我看到类似的问题因重复而被阻止,但我查看的其他 SO 答案要复杂得多。这真的很简单,正是我想要的。谢谢!
    • 我该如何适应echo 'a,"b1,b2",c'| awk -F',' '{print $2}'这样的逗号场景?
    • @clay 此代码假定中间列始终被引用,第一列从未被引用并且没有转义引号。
    • @melpomene,哎呀。这些都是不可接受的假设。我给出了一个不符合实际数据的简化示例。
    • @clay 那么你过度简化了你的例子。有几十种方法可以从您发布的输入中获取您发布的输出,而且几乎所有方法都不适用于您的真实输入。如果您想要一个稳健但简洁的答案,您需要提供更具代表性的充足输入/输出。
    【解决方案4】:

    你可以通过这种方式得到你想要的东西:

    awk '{split($0,a,/^"|" "| "|" |"$/);j=a[1]!=""?0:1;print a[2+j]}'
    

    我认为你可以找到失败的方法......

    【讨论】:

      猜你喜欢
      • 2018-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-20
      • 2018-02-19
      • 1970-01-01
      • 2017-06-14
      相关资源
      最近更新 更多