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;iawk
救援
$ 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”;如果这是要求,则建议的解决方案不可靠。如果他的数据