Bash 使用一列键/值对分析文件

Bash 使用一列键/值对分析文件,bash,parsing,awk,key-value,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中执行此操作吗 这里的问题是key-value列具有变量no-of条目,因此目标键在不同的行中具有不同的位置 我可以像这样grep给定键的值 grep -o 'sd=[

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

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中执行此操作吗

这里的问题是key-value列具有变量no-of条目,因此目标键在不同的行中具有不同的位置

我可以像这样grep给定键的值

grep -o 'sd=[^;]*' file.txt 
但我无法同时打印其他列值,因为:

$ 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
鉴于:

(这些是选项卡,不是空格)

您可以将
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

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
$awk-vk=“sd=“{n=split($NF,a,”;”);

为了(i=1;i
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
$awk-vk=“sd=“{n=split($NF,a,”;”);

对于(i=1;i我知道您要求使用awk,但这里是强制性的sed one liner,它比awk示例略短。在peaks提示之后,我在行的不同部分添加了几个带有
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=/
将第四列中的行与
sd=
匹配(作为第一个键或在
*;
之后),并将大括号内的以下部分应用于该行

大括号内的第二部分包括替换(
s
)和打印结果命令(
p

  • 四个
    *
    是您的列,第二列用括号括起来
  • (.*)?sd=([^;]+)
    捕获从
    sd=
  • 替换使用捕获的
    \1
    (第二列)和
    \3
    (sd=
后面的值)创建所需的输出
我知道您要求awk,但这里是强制性的sed one liner,它比awk示例略短。在peaks提示之后,我在行的不同部分添加了几个带有
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=/
将第四列中的行与
sd=
匹配(作为第一个键或在
*;
之后),并将大括号内的以下部分应用于该行

大括号内的第二部分包括替换(
s
)和打印结果命令(
p

  • 四个
    *
    是您的列,第二列用括号括起来
  • (.*)?sd=([^;]+)
    捕获从
    sd=
  • 替换使用捕获的
    \1
    (第二列)和
    \3
    (sd=
后面的值)创建所需的输出
    以下是避免拆分和循环的gawk/awk解决方案

    $ 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
    
    使用gawk,您可以使用gensub捕获组从
    $4
    中分离所需的值:

    $ gawk '/sd=/{print $2, gensub(/.*sd=([^;]*).*/,"\\1","g",$4)}' pf.txt
    cd 5
    ij 23
    bb 15
    
    或者,对于非gawk awk,您可以使用两个
    sub
    调用来删除所需值前后的零件:

    $ awk '/sd=/{ sub(/.*sd=/, "", $4); sub(/;.*/, "", $4); print $2, $4 }' pf.txt
    cd 5
    ij 23
    bb 15
    

    以下是避免拆分和循环的gawk/awk解决方案

    $ 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
    
    使用gawk,您可以使用gensub捕获组从
    $4
    中分离所需的值:

    $ gawk '/sd=/{print $2, gensub(/.*sd=([^;]*).*/,"\\1","g",$4)}' pf.txt
    cd 5
    ij 23
    bb 15
    
    或者,对于非gawk awk,您可以使用两个
    sub
    调用来删除所需值前后的零件:

    $ awk '/sd=/{ sub(/.*sd=/, "", $4); sub(/;.*/, "", $4); print $2, $4 }' pf.txt
    cd 5
    ij 23
    bb 15
    

    每当数据中有名称/值对时,最好从该数据创建名称/值数组,以便仅按名称引用值:

    $ 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
    

    每当数据中有名称/值对时,最好从该数据创建名称/值数组,以便仅按名称引用值:

    $ 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
    

    谢谢。这是一个很好的警告:最初的要求似乎是密钥等于“sd”;如果这是要求,那么使用
    $x==“sd”
    会更安全。谢谢。这是一个很好的警告:最初的要求似乎是密钥等于“sd”;如果这是要求,那么使用
    $x==“sd”
    会更安全。非常完美。谢谢警告:最初的要求似乎是密钥等于“sd”;如果这是要求,那么使用
    a[i]~k
    可能会带来麻烦。我没有看到锚定(它在
    if
    sub
    中都缺失)如果$2包含一个
    &
    \
    ,您的输出会让您大吃一惊。我不建议这样做,但YMMV取决于您的输入数据。非常完美。谢谢警告:最初的要求似乎是键等于“sd”;如果这是要求,则使用
    a[I]~k
    可能会自找麻烦。我没有看到锚定(if
和sub
中都缺少锚定)如果$2包含一个
&
\
,您将在输出中得到一个惊喜。我不建议这样做,但YMMV取决于您的输入数据。警告:最初的要求似乎是键必须等于“sd”;如果这是要求,则建议的解决方案不可靠。警告:最初的要求似乎是密钥等于“sd”;如果这是要求,则建议的解决方案不可靠。如果他的数据