ann*_*hri 3 bash awk perl text-processing
我想写一个数据解析器脚本。示例数据为:
name: John Doe
description: AM
email: john@doe.cc
lastLogon: 999999999999999
status: active
name: Jane Doe
description: HR
email: jane@doe.cc
lastLogon: 8888888888
status: active
...
name: Foo Bar
description: XX
email: foo@bar.cc
status: inactive
Run Code Online (Sandbox Code Playgroud)
键值对始终处于相同的顺序 ( name, description, email, lastLogon, status),但某些字段可能会丢失。也不保证第一条记录是完整的。
预期的输出是分隔符分隔的(例如 CSV)值:
John Doe,AM,john@doe.cc,999999999999999,active
Jane Doe,HR,jane@doe.cc,8888888888,active
...
Foo Bar,XX,foo@bar.cc,n/a,inactive
Run Code Online (Sandbox Code Playgroud)
我的解决方案是使用 whileread循环。我的脚本的主要部分:
name: John Doe
description: AM
email: john@doe.cc
lastLogon: 999999999999999
status: active
name: Jane Doe
description: HR
email: jane@doe.cc
lastLogon: 8888888888
status: active
...
name: Foo Bar
description: XX
email: foo@bar.cc
status: inactive
Run Code Online (Sandbox Code Playgroud)
这有效。但很明显,速度很慢。703行数据的执行时间:
real 0m37.195s
user 0m2.844s
sys 0m22.984s
Run Code Online (Sandbox Code Playgroud)
我正在考虑这种awk方法,但我没有足够的使用它的经验。
下面的awk程序应该可以工作。理想情况下,您将其保存到一个单独的文件(例如squash_to_csv.awk):
#!/bin/awk -f
BEGIN {
FS=": *"
OFS=","
recfields=split("name,description,email,lastLogon,status",fields,",")
}
function printrec(record) {
for (i=1; i<=recfields; i++) {
if (record[i]=="") record[i]="n/a"
printf "%s%s",record[i],i==recfields?ORS:OFS;
record[i]="";
}
}
$1=="name" && (FNR>1) { printrec(current) }
{
for (i=1; i<=recfields;i++) {
if (fields[i]==$1) {
current[i]=$2
break
}
}
}
END {
printrec(current)
}
Run Code Online (Sandbox Code Playgroud)
然后您可以将其称为
awk -f squash_to_csv.awk input.dat
John Doe,AM,john@doe.cc,999999999999999,active
Jane Doe,HR,jane@doe.cc,8888888888,active
Foo Bar,XX,foo@bar.cc,n/a,inactive
Run Code Online (Sandbox Code Playgroud)
这将在BEGIN块中执行一些初始化:
:后跟零个或多个空格”,如果name遇到该字段,它会检查它是否在文件的第一行,如果不是,则打印之前收集的数据。然后它将开始收集数组中的下一条记录current,从name刚刚遇到的字段开始。
对于所有其他行(为简单起见,我假设没有空行或注释行——但话又说回来,这个程序应该只是默默地忽略那些),程序检查行中提到了哪些字段,并将值存储在current用于当前记录的数组中的适当位置。
该函数printrec将这样的数组作为参数并执行实际输出。缺失值被替换为n/a(或您可能想要使用的任何其他字符串)。打印后,字段被清除,以便数组为下一组数据做好准备。
最后,还会打印最后一条记录。
笔记
:-space-combinations,则可以通过替换来强化程序
current[i]=$2
Run Code Online (Sandbox Code Playgroud)
经过
sub(/^[^:]*: */,"")
current[i]=$0
Run Code Online (Sandbox Code Playgroud)
这会将值设置为“第一个:空格组合之后的所有内容”,方法是删除 ( sub) 直到包括行上第一个:空格组合的所有内容。,),您将必须采取适当的措施来转义该字符或引用输出,具体取决于您要遵守的标准。