![如果 awk 中不存在字段则添加回车符](https://linux22.com/image/205428/%E5%A6%82%E6%9E%9C%20awk%20%E4%B8%AD%E4%B8%8D%E5%AD%98%E5%9C%A8%E5%AD%97%E6%AE%B5%E5%88%99%E6%B7%BB%E5%8A%A0%E5%9B%9E%E8%BD%A6%E7%AC%A6.png)
我在用
awk -F'[":]' '$2=="id"{printf("pri,%s,",$5)}$2=="name"{printf("%s,",$5)}$2=="objectId"{printf$4}$2=="polledName"{print$5}' | sed -e 's/, /,/g'
这变成了
}, {
"id" : "1",
"name" : "host1",
"objectId" : 0001,
"polledName" : "192.168.1.1"
}, {
"id" : "2",
"name" : "host2",
"objectId" : 0002,
"polledName" : "192.168.1.2"
}, {
"id" : "3",
"name" : "host3",
"objectId" : 0003,
}, {
"id" : "4",
"name" : "host4",
"objectId" : 0004,
"polledName" : "192.168.1.3"
}, {
进入这个
pri,1,host1,0001,192.168.1.1
pri,2,host2,0002,192.168.1.2
pri,3,host3,0003,pri,4,host4,0004,192.168.1.3
知道如何对此进行修改,以便当 polledName 的条目不存在时,它会转到下一行而不是换行到当前行,即如果 $5 什么都不返回,则添加换行符。
下面是上面的代码,其中 awk 脚本经过精心打印,gawk -o-
因此清晰易读:
awk -F'[":]' '
$2 == "id" {
printf "pri,%s,", $5
}
$2 == "name" {
printf "%s,", $5
}
$2 == "objectId" {
printf $4
}
$2 == "polledName" {
print $5
}
' | sed -e 's/, /,/g'
答案1
对于任何人做可以访问在命令行上使用 JSON 的适当工具,您可以使用以下方法将字段提取到引用的 CSV 数据集中jq
:
$ jq -r '.[] | [ "pri", .id, .name, .objectId, .polledName ] | @csv' file
"pri","1","host1",1,"192.168.1.1"
"pri","2","host2",2,"192.168.1.2"
"pri","3","host3",3,
"pri","4","host4",4,"192.168.1.3"
这假设问题中显示的数据是顶级数组的一部分并且格式正确(问题中的第三个元素包含无效的尾随逗号):
[
{"id":"1","name":"host1","objectId":1,"polledName":"192.168.1.1"},
{"id":"2","name":"host2","objectId":2,"polledName":"192.168.1.2"},
{"id":"3","name":"host3","objectId":3},
{"id":"4","name":"host4","objectId":4,"polledName":"192.168.1.3"}
]
如果您想要用带引号的空字符串代替没有什么对于缺失.polledName
值,将.polledName
表达式更改jq
为.polledName // ""
。null
如果键不可用(或其值为) ,这将使用空字符串而不是值null
。
更改@csv
为@tsv
输出运算符以获取制表符分隔的值。
使用 JSON 感知工具执行此操作的好处是,您将在输出中获得解码后的字符串,而不是 JSON 编码的数据。此外,嵌入的引号等会自动正确处理,并且 JSON 输入是否在单行上或以其他特殊方式格式化并不重要。
答案2
如果您确实必须使用awk
,请为 polledName 设置/取消设置变量
awk -F'[":]' '$2 == "id" {if(lf) print "" ; printf("pri,%s,",$5); lf=1;}
$2 == "name" {printf("%s,",$5)}
$2 == "objectId" {gsub(" ","",$4); printf "%s", $4}
$2 == "polledName" {print $5; lf=0; }
END {if(lf) print "" ;}'
这基本上是你的代码有点扩展,我添加的是:
- 带有“id”的行
if(lf) print "" ;
打印一个新行(如果lf
不为零);lf=1
: 放lf
- 带有“polledName”的可选行:
lf=0;
遇到行时清除 lf。 - 根据注释使用
print ""
(请注意,print
不带参数打印当前/最后一行) - 添加
gsub(" ","",$4);
到 $4 的条带空间(由于分隔符非空格而保留空格)
请注意,这awk
是解析 json(或 xml)文件的糟糕解决方案。
您依赖于生成此 json 文件的程序,字段顺序可能会发生变化,特别是如果您位于封闭的服务器或设备中。
答案3
使用任何 awk,即使行的顺序可能有所不同和/或可能会丢失不同的行或附加行:
$ awk -F'(^ *")|("?,?$)|(" : "?)' -v OFS=',' '
/}, {/ {
if ( NR>1 ) {
print "pri", f["id"], f["name"], f["objectId"], f["polledName"]
}
delete f
next
}
{ f[$2] = $3 }
' file
pri,1,host1,0001,192.168.1.1
pri,2,host2,0002,192.168.1.2
pri,3,host3,0003,
pri,4,host4,0004,192.168.1.3
上面的工作原理是创建一个数组,每次读取一行时f[]
将每个标签(例如objectId
)映射到同一行上的关联值(例如) ,然后在每个块的末尾(即何时看到)打印该数组的内容。002
tag : value
}, {
创建像这样的标签值数组是解决此类问题的一种比打印值更好的通用方法,因为它与输入中标签的顺序无关,您可以决定输出的一些不同顺序,并且您可以使用数组中存储的值来测试条件并在当前打印块中采取操作,例如
if ( f["objectId"] > 27 ) print "The objectId is too big for name", f["name"]
if ( !("polledName" in f) ) print "polledName missing for ID", f["id"]
if ( f["objectId"] ~ /7/) && (f["id"] !~ /7/) ) {
printf "objectId %s vs id %s mismatch\n", f["objectId"], f["id"]
}
或者您可能感兴趣的任何真实条件[组合]。
或者,如果保证每个块中的行顺序在所有块中保持一致,则可以使用 GNU awk 进行多字符 RS,如示例所示:
$ awk -v RS='}, {\n' -F'("?,?\n)|(" : "?)' -v OFS=',' '
NR>1 { print "pri", $2, $4, $6, $8 }
' file
pri,1,host1,0001,192.168.1.1
pri,2,host2,0002,192.168.1.2
pri,3,host3,0003,
pri,4,host4,0004,192.168.1.3
关于printf $4
代码中的等 - 永远不要这样做printf input_data
,因为如果/当输入数据包含 printf 格式字符时它会失败,总是这样做printf "%s", input_data
,例如printf "%s", $4
.
另外,当您使用 awk 时,您永远不需要 sed,因此如果您发现自己这样做,那么您的方法是错误的。