我正在尝试解析制表符分隔文件,最后一列具有由分号分隔的可变数量的键值对。这是一个例子
ab cd ef as=2;sd=5;df=12.3
gh ij kl sd=23;df=55
mn op qr as=24;df=77
我想要打印第二列以及与键相关联的值" sd" 预期的输出应该是
cd 5
ij 23
我可以用bash做到这一点吗?
这里的问题是键值列具有变量no条目,因此目标键在不同的行中具有不同的位置。
我可以像这样点击给定键的值
grep -o 'sd=[^;]*' file.txt
但我无法同时打印其他列值
答案 0 :(得分:2)
每当数据中有名称/值对时,最好从该数据创建一个名称/值数组,这样您就可以按名称引用这些值:
$ cat tst.awk
{
delete n2v
split($NF,tmp,/[;=]/)
for (i=1;i in tmp;i+=2) {
n2v[tmp[i]] = tmp[i+1]
}
}
"sd" in n2v { print $2, n2v["sd"] }
$ awk -f tst.awk file
cd 5
ij 23
答案 1 :(得分:1)
awk
救援!
$ awk -v k="sd=" '{n=split($NF,a,";");
for(i=1;i<=n;i++)
if(a[i]~k)
{sub(k,$2" ",a[i]);
print a[i]}}' file
cd 5
ij 23
如果你的钥匙没有固定长度,那么将它固定在左边是一个更好的主意。
将a[i]~k
更改为a[i]~"^"k
答案 2 :(得分:1)
我知道你要求awk,但这里是必须的sed one liner,比awk示例短一点。在峰值提示之后,我在该行的不同部分添加了一些sd
的测试用例。
cat kv.txt
ab cd ef as=2;sd=5;df=12.3
gh ij kl sd=23;df=55
test1 sd in col2=true;df=55
test2 sd_inFront spacer sd=2;other=5;
test3 sd_inMiddle spacer other1=6;sd=3;other2=8
test4 sd_atEnd spacer other1=7;sd=4;
test5 sd_AtEndWO; spacer other1=8;sd=5
test6 esd in col4=true;esd=6;
test7 esd_inFront spacer esd=7;other=5;
test8 esd_inMiddle spacer other1=6;esd=8;other2=8
test9 esd_atEnd spacer other1=7;esd=9;
test10 esd_AtEndWO; spacer other1=8;esd=10
test11 sd_and_esd spacer other1=6;sd=11;other2;esd=4;other3=8
test12 esd_and_sd spacer other1=6;esd=3;other2;sd=12;other3=8
cat kv.txt| sed -nr "/(.+\w){3} (.*;)?sd=/ {s/.* (.*) .* (.*;)?sd=([^;]+).*/\1 \3/g; p;}"
cd 5
ij 23
sd_inFront 2
sd_atEnd 4
sd_AtEndWO; 5
sd_and_esd 11
esd_and_sd 12
sed命令由两部分组成:第一部分/(.+\w){3} (.*;)?sd=/
匹配第4列中的sd=
行(作为第一个键或.*;
之后)并在内部应用以下部分线上的大括号。
大括号内的第二部分由替换(s
)和打印结果命令(p
)组成。替换的工作方式如下:
.*
是您的列,第二列是使用括号(.*;)?sd=([^;]+)
捕获sd=
后的值;
\1
(第二列)和\3
(sd=
之后的值)来创建所需的输出答案 3 :(得分:0)
假设:
$ cat /tmp/file.txt
ab cd ef as=2;sd=5;df=12.3
gh ij kl sd=23;df=55
mn op qr as=24;df=77
mn sd qr as=24;df=77
(那些是制表符,而不是空格)
您可以将awk
设置为选项卡或;
上的字段,如下所示:
$ awk -F "\t|;" '/sd/ {print $2}' /tmp/file.txt
cd
ij
sd
(我意识到最后一个不应该打印;忍受我)
然后打印具有'sd'的字段,只需遍历字段:
$ awk -F "\t|;" '/sd/ { for (x=1;x<=NF;x++) if ($x~"^sd=") print $2 " " $(x) }' /tmp/file.txt
cd sd=5
ij sd=23
然后,您可以在=
上拆分该字段,更改$x~"^sd="
以获得完全匹配,然后在=
两侧的拆分右侧打印字段,以获得准确的结果输出:
$ awk -F "\t|;" '/sd/ { for (x=1;x<=NF;x++) if ($x~"^sd=") { split($x, tmp, /=/); print $2 " " tmp[2]}}' /tmp/file.txt
cd 5
ij 23
答案 4 :(得分:0)
以下是gawk / awk解决方案,可避免分裂和循环。
1.Windows
2.Ubuntu *Debian
3.RPM
4.Sources
使用gawk,您可以使用gensub捕获组从$ cat pf.txt
ab cd ef as=2;sd=5;df=12.3
gh ij kl sd=23;df=55
aa bb cc as=24;df=77;sd=15
mn op qr as=24;df=77
中隔离所需的值:
$4
或者,使用非gawk awk,您可以使用两个$ gawk '/sd=/{print $2, gensub(/.*sd=([^;]*).*/,"\\1","g",$4)}' pf.txt
cd 5
ij 23
bb 15
调用来删除所需值之前和之后的部分:
sub