【问题标题】:Using AWK to process CSV file with rows containing an array使用 AWK 处理包含数组的行的 CSV 文件
【发布时间】:2022-11-25 02:14:35
【问题描述】:

尝试使用 AWK 处理 CSV 文件,但是我遇到了一个问题,我行中的许多单元格已经包含逗号 ,,这意味着我无法使用 awk -F, 分隔字段。

CSV文件

Name,...DATE,COLUMNX,ADDRESSES
host1,...,NOV 24, 2022,['Element1', 'Element2'],"['192.168.x.99', 'fe80:XX','192.168.x.100', fe80:XX]"
host2,...,NOV 24, 2022,['Element3'],"['192.168.x.101', 'fe80:XX']"

... 表示包含[,'" 的行/列

我试过的:
awk -F, '{print $X}'
这给了我以下输出:

'Element2']
"['192.168.x.101'

我想要完成的事情:

host1 192.168.x.99
host1 192.168.x.100
host2 192.168.x.101

【问题讨论】:

  • ,...,代表多少列?另外,是否有任何列在包含所需 IP 地址之前有 "['
  • 是的,我已经更新了问题。
  • 谢谢。我将发布一种可行的方法,但您可能必须更改列号才能获得所需的部分。
  • 您的 CSV 无效:应引用包含逗号的字段(请参阅 ADDRESSES 字段)

标签: awk


【解决方案1】:

我会推荐一个合适的 CSV 解析器来完成这项工作,然后使用 awk 来做正则表达式,例如

$ ruby -r 'csv' -ne 'lines=$_
  CSV.parse(lines) do |i| 
    i.each do |j| 
      printf("%s ", j)
    end
    puts ""
  end' file | 
awk '{gsub(/['|']|'|]|,/, "", $0)}
  /^host/{for(i=1;i<=NF;i++){if($i~/^[0-9]+.+/){print $1, $i}}}'
host1 192.168.x.99
host1 192.168.x.100
host2 192.168.x.101

【讨论】:

  • OPs 输入不是任何“标准”的正确 CSV,因此 YMMV 试图在其上使用正确的 CSV 解析器。
  • @EdMorton 啊,文件已更新。我使用了具有正确引用的旧版本。
【解决方案2】:

使用awk

awk -F","|"$" 'NR>1 { 
gsub(/'|[[]]/,""); 
split($2,a,", "); 
split($1,h,","); 
for (n in a) {if (a[n] ~ /^[0-9]/) printf "%s %s
", h[1], a[n]}}' src.csv

输出:

host1 192.168.x.100
host1 192.168.x.99
host2 192.168.x.101

细节:

-F","|"$"(在记录末尾拆分,"或"(将删除尾随双引号,每条记录将拆分为两个字段。

gsub(/'|[[]]/,"");(通过删除单引号和/或括号进行清理)

split($2,a,", ");(在逗号空间将第二个字段拆分为数组a

split($1,h,",");(在逗号上将第一个字段拆分为数组h

for (n in a) {if (a[n] ~ /^[0-9]/) printf "%s %s ", h[1], a[n] 遍历数组 a 并且仅当数组项以数字开头时才打印输出

【讨论】:

    【解决方案3】:

    现代版本的 awk 允许在多个字段分隔符处拆分记录。因此,每一行都可以用逗号和单引号分隔,以隔离您需要的数据。

    要将 ', 一起用作字段分隔符,需要对前者进行转义,然后将两者结合起来可能会非常棘手。经过几次试验后,我发现最简单的方法是使用带有正则表达式的 shell F 开关,包括转义的 ',。这很麻烦,因为您必须在转义所需的单引号并重新打开单引号命令之前关闭第一个单引号:-F'[,'''=]'(我通常更喜欢在 awk 过程中设置字段分隔符,但这个让我失望了)。

    此编辑版本用于隔离该字段(更改 $35 以适应试错):

    awk -F'[,''']' 'NR>1{print $1" "$35}' data.csv
    

    我在以下测试文件上测试了以上内容:

    data.csv:
    Name,...DATE,COLUMNX,ADDRESSES
    host1,['El3', 'El6'],['El7', 'El12'],['El1', 'El2'],['El', 'E12'],NOV 24, 2022,['Element1', 'Element2'],"['192.168.x.99', 'fe80:XX','192.168.x.100', fe80:XX]"
    host2,['El3', 'El6'],['El7', 'El12'],['El1', 'El2'],['El', 'E12'],NOV 24, 2022,['Element1', 'Element2'],"['192.168.xxx.yy', 'fe80:XX','192.168.x.100', fe80:XX]"
    host3,['El3', 'El6'],['El7', 'El12'],['El1', 'El2'],['El', 'E12'],NOV 24, 2022,['Element1', 'Element2'],"['192.xxx.x.99', 'fe80:XX','192.168.x.100', fe80:XX]"
    host4,['El3', 'El6'],['El7', 'El12'],['El1', 'El2'],['El', 'E12'],NOV 24, 2022,['Element1', 'Element2'],"['xxx.168.x.99', 'fe80:XX','192.168.x.100', fe80:XX]"
    

    输出:

    host1 192.168.x.99
    host2 192.168.xxx.yy
    host3 192.xxx.x.99
    host4 xxx.168.x.99
    

    【讨论】:

    • 抱歉,我在原帖中无意中使用了 $0 而不是 $1,已编辑更正。
    • FS 语句中的' 应该被转义吗?
    • 不,但我刚刚也意识到可能不需要 OR |,因为方括号已经表示 OR 了。引号应该是正则表达式边界//。真的很抱歉,很匆忙,没有时间制作测试文件,但我认为设置两个定界符会得到你需要的。
    • 不幸的是@DavePritlove,我和我最初的尝试一样接近:awk -F, '{print $X}'
    • 好的,我得冲了,但稍后再看。我确信设置两个分隔符是可行的方法,因为它会隔离你的输出部分,它只是让正则表达式适合'OR',你可以尝试通过 BEGIN 块删除并将你的 shell 参数设置为-F ' | ,'|, 周围有和没有定界引号或括号。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-20
    • 1970-01-01
    • 2011-03-09
    • 1970-01-01
    • 1970-01-01
    • 2012-01-17
    • 1970-01-01
    相关资源
    最近更新 更多