Bash 从可变长度换行输入创建csv
看起来很简单,但我已经玩了一段时间,找不到优雅的东西 所以我有这样的数据:Bash 从可变长度换行输入创建csv,bash,awk,Bash,Awk,看起来很简单,但我已经玩了一段时间,找不到优雅的东西 所以我有这样的数据: Field1 09:30 Field2 H Field3 Happy Field1 09:35 Field3 Sad Field1 09:40 Field2 C Field1 09:45 Field2 P Field3 Pleased awk -f csv.awk input.txt 。。。基本上,字段1将始终存在,其他字段是可选的。我想将其拆分为csv(遗憾的是我不能使用python),以便适当地保留空格 09:3
Field1 09:30
Field2 H
Field3 Happy
Field1 09:35
Field3 Sad
Field1 09:40
Field2 C
Field1 09:45
Field2 P
Field3 Pleased
awk -f csv.awk input.txt
。。。基本上,字段1将始终存在,其他字段是可选的。我想将其拆分为csv(遗憾的是我不能使用python),以便适当地保留空格
09:30, H, Happy
09:35, , Sad
09:40, C,
09:45, P, Pleased
让我们试试这个:
awk 'BEGIN{OFS=", "}
p && /Field1/
{ print a["Field1"], a["Field2"], a["Field3"];
a["Field1"]=a["Field2"]=a["Field3"]=""
}
{a[$1]=$2; p=1}
END{print a["Field1"], a["Field2"], a["Field3"]}
' file
它返回:
$ awk 'BEGIN{OFS=", "} p && /Field1/ {print a["Field1"], a["Field2"], a["Field3"]; a["Field1"]=a["Field2"]=a["Field3"]=""} {a[$1]=$2; p=1} END{print a["Field1"], a["Field2"], a["Field3"]}' file
09:30, H, Happy
09:35, , Sad
09:40, C,
09:45, P, Pleased
解释
将输出字段分隔符设置为BEGIN{OFS=“,”}
(逗号,空格),
如果p&&/Field1/{}
标志为“开”,且该行包含p
,则执行Field1
{}
打印打印一个[“Field1”]、一个[“Field2”]、一个[“Field3”]
数组的三个值a[]
清空数组a[“Field1”]=a[“Field2”]=a[“Field3”]=”
对于每一行,将第二列值存储在{a[$1]=$2;p=1}
数组中。另外,激活a[]
标志,以便在找到下一个p
时开始打印行字段1
打印最后一段数据END{print a[“Field1”]、a[“Field2”]、a[“Field3”}
- 这里有一个与
awk
相关的替代方案:
# new records always starting with Field1
/Field1/ {
# print record if it isset
if(length(r[0])>0) {
printf "%s, %s, %s\n", r[0], r[1], r[2]
};
# reinitialize record
r[0]=r[1]=r[2]=""
# copy value
r[0]=$2
}
/Field2/ {
# copy value
r[1]=$2
}
/Field3/ {
# copy value
r[2]=$2
}
# the END block idea comes from @fedorqui. Thanks!
END {
# print record if it isset
if(length(r[0])>0) {
printf "%s, %s, %s\n", r[0], r[1], r[2]
};
}
将脚本保存在csv.awk
中。然后像这样执行awk
:
Field1 09:30
Field2 H
Field3 Happy
Field1 09:35
Field3 Sad
Field1 09:40
Field2 C
Field1 09:45
Field2 P
Field3 Pleased
awk -f csv.awk input.txt
下面是
perl
中的一个解决方案:
perl -lane 'if(/Field([\d])/){
if($1==1 && $.!=1)
{
print join ",",@a;
undef @a
}
$a[$1-1]=$F[1]}
END{print join ",",@a}' your_file
/Field([\d])/
-[\d]周围的大括号将捕获$1中的数字,以后可以将其用作数组索引
$a[$1-1]=$F[1]->
将行的第二个字段存储在前面捕获的索引处
if($1==1 && $.!=1)
{
print join ",",@a;
undef @a
}#
捕获的十进制数为1时立即打印数组,并清空数组
最后会留下一个数组,该数组将在end
块中打印
测试如下:
> cat temp
Field1 09:30
Field2 H
Field3 Happy
Field1 09:35
Field3 Sad
Field1 09:40
Field2 C
Field1 09:45
Field2 P
Field3 Pleased
> perl -lane 'if(/Field([\d])/){if($1==1 && $.!=1){print join ",",@a;undef @a }$a[$1-1]=$F[1]}END{print join ",",@a}' temp
09:30,H,Happy
09:35,,Sad
09:40,C
09:45,P,Pleased
>
看来你和我一样厌倦了同样的数据格式化工作。
下面是我经常使用的代码片段,稍加修改。您可以在第2行中定义任意数量的字段(不一定是相同的模式),然后发出
awk-f so.awk input.txt
(假设您将此awk片段保存在so.awk中,并且输入文件为input.txt)
享受:)
再来一个
function dump() { if (a[1]!="") print a[1],a[2],a[3]; a[2]=a[3]=""; };
BEGIN { RS="Field"; OFS=", "; a[1]=a[2]=a[3]="" }
END {dump()}
{ if ($1=="1") dump(); a[$1]=$2; }
对必须添加
END
块来打印最后的信息:)找不到更好的方法!是的,只需捕获小数点后字段并将其用作数组中的索引$a[$1-1]=$F[1]
。简单!不是吗?我不会说awk
不简单。确实如此,只是数据的获取有点长。简言之:)谢谢,伙计,这很有效。但是有两个问题!首先,当我把它放到bash脚本中时,它的行为会有所不同,你知道为什么吗?我还尝试将其改编为我的真实脚本(实际上有四个字段),但无法获得正确的输出out@mbbxedh2嗯,请确保正确复制和粘贴它。您还可以将其保存在script.awk
中,并使用awk-f script.awk运行您的_文件
。对于实际情况,需要考虑清空所有4个字段。试着发布一些相关的信息(以防你不想发布真实的数据),这样我就能猜出哪里出了问题。成功!