使用一列键/值对解析文件

时间:2016-01-16 14:30:47

标签: bash parsing awk key-value

我正在尝试解析制表符分隔文件,最后一列具有由分号分隔的可变数量的键值对。这是一个例子

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 

但我无法同时打印其他列值

5 个答案:

答案 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(第二列)和\3sd=之后的值)来创建所需的输出

答案 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