我正在尝试编写一个bash脚本来编辑一个json文件来删除整个对象,但是我已经碰壁了。
这里是样本数据
[
{
"MapObject" : {
"BoundingBox" : [ -1313.574, -1010.804, -1113.574, -810.804 ],
"Caption" : "Ref46",
"Comment" : "",
"Position" : {
"CPosition" : [ -1213.574, -910.804, 0, 0 ]
},
"Size" : {
"CPosition" : [ 100, 100, 0, 0 ]
},
"Type" : "moReferencePointID"
}
},
{
"MapObject" : {
"BoundingBox" : [ -1313.06, 495.39, -1113.06, 695.39 ],
"Caption" : "Ref47",
"Comment" : "",
"Position" : {
"CPosition" : [ -1213.06, 595.39, 0, 0 ]
},
"Size" : {
"CPosition" : [ 100, 100, 0, 0 ]
},
"Type" : "moReferencePointID"
}
},
{
"MapObject" : {
"BoundingBox" : [ -18070, 1180, -17870, 1380 ],
"Caption" : "Path way84",
"Comment" : "",
"ExecutableMapObject" : {
"BehaviourList" : [
{
"Speed" : 200,
"Type" : "bcSpeed"
},
{
"AvoidanceSpeed" : 150,
"FailureAction" : 0,
"FailureRetries" : 60,
"FailureWait" : 10,
"PathMode" : 2,
"PerSegment" : 15,
"ReturnToStart" : 0,
"ScanAngle" : 0.06981317007977778,
"Type" : "bcObstacleAvoidance",
"Wait" : 10,
"WaitUnits" : 2
},
{
"BinarySensorStates" : 3,
"SensorSensitivityList" : [
{
"IgnoreMapData" : false,
"Mask" : 65536,
"SpeedCapDistance" : 70,
"SpeedCapEnabled" : true,
"StopDistance" : 70
},
{
"IgnoreMapData" : false,
"Mask" : 131072,
"SpeedCapDistance" : 70,
"SpeedCapEnabled" : true,
"StopDistance" : 70
},
{
"IgnoreMapData" : false,
"Mask" : 262144,
"SpeedCapDistance" : 240,
"SpeedCapEnabled" : true,
"StopDistance" : 240
},
{
"IgnoreMapData" : false,
"Mask" : 524288,
"SpeedCapDistance" : 240,
"SpeedCapEnabled" : true,
"StopDistance" : 240
},
{
"IgnoreMapData" : false,
"Mask" : 1048576,
"SpeedCapDistance" : 240,
"SpeedCapEnabled" : true,
"StopDistance" : 240
},
{
"IgnoreMapData" : false,
"Mask" : 2097152,
"SpeedCapDistance" : 280,
"SpeedCapEnabled" : true,
"StopDistance" : 280
},
{
"IgnoreMapData" : false,
"Mask" : 4194304,
"SpeedCapDistance" : 320,
"SpeedCapEnabled" : true,
"StopDistance" : 320
},
{
"IgnoreMapData" : false,
"Mask" : 8388608,
"SpeedCapDistance" : 0,
"SpeedCapEnabled" : false,
"StopDistance" : 0
},
{
"IgnoreMapData" : false,
"Mask" : 16777216,
"SpeedCapDistance" : 0,
"SpeedCapEnabled" : false,
"StopDistance" : 0
}
],
"SpeedCap" : 30,
"Type" : "bcObstacleSensitivity2"
}
],
"EntryPoint" : {
"CPosition" : [ -13450, 1290, 0, 0 ]
},
"ExitPoint" : {
"CPosition" : [ -17970, 1280, 0, 0 ]
},
"RelocateOnExecute" : false,
"Required" : false,
"TwoWay" : false
},
"GoalSet" : [
{
"Path" : {
"PathType" : "pitLinear",
"Segments" : [
{
"Segment" : [ -13450, 1290, -17970, 1280 ]
}
]
}
}
],
"Position" : {
"CPosition" : [ -17970, 1280, 0, 0 ]
},
"Size" : {
"CPosition" : [ 0, 0, 0, 0 ]
},
"Type" : "moLinePathID"
}
},
{
"MapObject" : {
"AttributeList" : [
[ "Lock", "{} LabInt {}" ]
],
"BoundingBox" : [ -12350, -500, -12150, -300 ],
"Caption" : "QUEUE/LabInt",
"Comment" : "",
"ExecutableMapObject" : {
"EntryPoint" : {
"CPosition" : [ -12250, -400, 0, 0 ]
},
"ExitPoint" : {
"CPosition" : [ -12250, -400, 0, 0 ]
},
"RelocateOnExecute" : false,
"Required" : false,
"TwoWay" : false
},
"Position" : {
"CPosition" : [ -12250, -400, 0, 0 ]
},
"Size" : {
"CPosition" : [ 100, 100, 0, 0 ]
},
"Type" : "moHotPointID"
}
},
]
每个对象都以
开头 {
以
结束 },
我想删除包含“Type”的所有对象及其数据:“moReferencePointID”和“moReferenceLineID”。
某些对象中有多个Type实例。
我一直在使用sed和awk使用我在网上找到的东西,并且不能完全使用它。任何帮助将不胜感激。
我发布了迄今为止我所做的事情,但它绝对无效......
答案 0 :(得分:1)
正则表达式是解析结构化文本的错误答案;使用解析器是正确的答案。将整个文件读入内存也是错误的答案,但这就是解析器所做的事情,所以meh。
那就是说,错误的答案有时候是可以的:
了解何时不使用正则表达式以及如何使用更强大,更可靠的替代方案非常重要:但是当您真正需要它们时,了解如何使用正则表达式完成任务可以成为救星。< / p>
至少,这是一个有趣的教学练习,可以使用各种unix命令行实用程序来探索正则表达式和多行文本块处理。
<强> PHP:强>
php -r '$a=file_get_contents("data.json");file_put_contents("cleaned.json", preg_replace("/\{\s*\"MapObject(?:(?!MapObject)[\s\S])+\"Type\"\s*:\s*\"moReference(?:Point|Line)ID\"(?:(?!MapObject)[\s\S])+},?\s*/", "", $a));'
<强>的Perl:强>
$_=do{local $/;<>};
只是让它在一行中读取文件。
perl -e '$_=do{local $/;<>};$_=~s/\{\s*\"MapObject(?:(?!MapObject)[\s\S])+\"Type\"\s*:\s*\"moReference(?:Point|Line)ID\"(?:(?!MapObject)[\s\S])+},?\s*//g;open($fh,">","cleaned.json");print $fh $_;' data.json
<强> awk中:强>
RS="<{EOF}>"
只是让它作为一行读取文件。将字符串替换为绝对不会出现在数据集中的任何字符串。
awk -v RS="<{EOF}>" '{gsub(/\{[\r\n\t ]*\"MapObject/, "\x1E&");print $0}' < data.json > data1.json
awk -v RS="<{EOF}>" '{gsub(/\x1E[^\x1E]*\"Type\"[\t ]*:[\t ]*\"moReference(Point|Line)ID\"[^\x1E]*},[\r\n\t ]*|\x1E/, "");print $0}' < data1.json > cleaned.json
<强>桑达:强>
-e 1h;2,$H;$!d;g
只是让它在一行中读取文件。
sed -e '1h;2,$H;$!d;g' -E -e 's/\{\s*\"MapObject/\x1E\0/g' < data.json > data2.json
sed -e '1h;2,$H;$!d;g' -E -e 's/\x1E|\{[^\x1E]*\"Type\"\s*:\s*\"moReference(Point|Line)ID\"[^\x1E]*},\s*//g' < data2.json > cleaned.json
OSX Sed
echo 'Just use vi' | sed -e '' && vi data.json
在上面的所有情况中,我已经完成了将整个文件作为单个字符串读取所花费的一切,然后全局替换正则表达式与空字符串匹配的内容。
这些解决方案依赖于这样一个事实:每个对象都以可识别的开放模式\{\s*"MapObject"\s*:\s*\{
开头,在每个对象的开头只出现一次。
PHP和Perl可以直接使用它,使用负前瞻断言。
然而,Sed和Awk并不支持环顾四周的断言,也不支持非贪婪的匹配。所以,我需要一个单宽度标记,所以我必须在多个传递中执行:一个将一些标记字符附加到开始模式(我选择了0x1E,ascii控制代码为&#34;记录分隔符&#34;) ;和一个做实际更换。
如果您的代码可能包含任意字节值,而不是可读文本,那么您将需要另外两次传递,首先将所有0x1E替换为不会出现在文本中的字符串,然后将该字符串替换为结束。
Awk似乎也不支持(在我的OSX机器上)空格的转义,所以我使用了字符组。
正则表达式可以描述为:
\{\s*"MapObject" The opening pattern, starting an object.
(?:(?!MapObject)[\s\S])+ Some characters, not the opening pattern.
\"Type\" Literal quoted string "Type".
\s*:\s* Whitespace wrapped literal colon ':'.
\"moReference Literal doublequote and start of string.
(?: Non-capturing group of...
Point ...literal string 'Point'...
| ...or...
Line ...literal string 'Line'.
) Finish that logical grouping.
ID\" End of literal string and doublequote.
(?:(?!MapObject)[\s\S])+ More characters, not the opening pattern.
} Closing brace.
,? Optional comma.
\s* Optional trailing whitespace.
你可以在这里看到这个可怕的野兽,可以看到使用包含多个类型行的对象:https://regex101.com/r/XGULr5/4
答案 1 :(得分:0)
jq是你的朋友。它的食谱has an example应该适应这一点。
答案 2 :(得分:0)
{
if ($0 ~ /{/) {
strt=1;cnt++
}
if (strt=1) {
print cnt
bits[cnt]=bits[cnt]"\n"$0
if ( $0 ~ /Type/ ) {
typ[cnt]=$0
}
}
if ( $0 ~ /}/ ) {
strt=0
}
}
END{
for ( i=1;i<=cnt;i++ ) {
if ( typ[i] ~ /NotGood/ || typ[i] ~ /Bad/ ) {
}
else {
printf bits[i]
}
}
}
上面的awk代码应该可以工作(使用awk -f codefile datafile运行)。基本上我们检查“{”的每一行(模式匹配),然后开始将每一行写入一个数组(位),直到“}”模式匹配。除此之外,还会编写一个附加数组(典型值)来跟踪模式匹配“类型”的行。在代码的最后,位数组循环,引用typ。如果typ包含“NotGood”,则忽略位数,否则打印。
作为一个班轮,解决方案将是:
awk '{ if ($0 ~ /{/) { strt=1;cnt++ } if (strt=1) { pnt;bits[cnt]=bits[cnt]"\n"$0;if ( $0 ~ /Type/ ) { typ[cnt]=$0 } } if ( $0 ~ /}/ ) { strt=0 } } END { for ( i=1;i<=cnt;i++ ) { if ( typ[i] ~ /NotGood/ || typ[i] ~ /Bad/ ) { } else { printf bits[i] } } }' filename