我有一串用冒号分隔的作业,每个作业的形式都是a=b
.我需要解析它以提取 foo,其中 foo 是...:di=foo:...
。赋值di=foo
可以发生在字符串的开头、中间或末尾。
我的想法是匹配行的开头或冒号,然后匹配字符串di=
,然后匹配除冒号之外的每个字符,然后匹配冒号或行尾。
我只设法让“除冒号之外的每个字符”部分正常工作。
一些测试:
echo "di=a;b:*.di=c;d:ddi=e;f" | sed "s/.*di=\([^:]*\):.*/\1/"
echo "ddi=a;b:di=c;d:*.di=e;f" | sed "s/.*di=\([^:]*\):.*/\1/"
echo "*.di=a;b:ddi=c;d:di=e;f" | sed "s/.*di=\([^:]*\):.*/\1/"
第一个应该返回a;b
,第二个c;d
和第三个应该返回e;f
,但现在它们都返回了c;d
。
答案1
我的想法是匹配行首或冒号,然后匹配字符串 di=,然后匹配除冒号之外的每个字符,然后匹配冒号或行尾。
您不需要匹配“然后是冒号或行尾”(如您的示例中所示)。
{
echo "di=a;b:*.di=c;d:ddi=e;f"
echo "ddi=a;b:di=c;d:*.di=e;f"
echo "*.di=a;b:ddi=c;d:di=e;f"
} | sed 's/\(^\|.*:\)di=\([^:]*\).*/\2/'
输出:
a;b
c;d
e;f
\(^\|.*:\)
匹配行的开头或后跟冒号的任何字符
答案2
对于这样的情况,我倾向于欺骗:
并在前面和末尾添加一个,因此删除特殊情况;匹配现在总是为:a=foo:
所以:
sed -e 's/^/:/' -e 's/$/:/' -e 's/.*:di=\([^:]*\):.*/\1/'
是可以优化的
sed -e 's/^\(.*\)$/:\1:/' -e 's/.*:di=\([^:]*\):.*/\1/'
结果:
% echo "di=a;b:*.di=c;d:ddi=e;f" | sed -e 's/^/:/' -e 's/$/:/' -e 's/.*:di=\([^:]*\):.*/\1/'
a;b
% echo "ddi=a;b:di=c;d:*.di=e;f" | sed -e 's/^/:/' -e 's/$/:/' -e 's/.*:di=\([^:]*\):.*/\1/'
c;d
echo "*.di=a;b:ddi=c;d:di=e;f" | sed -e 's/^/:/' -e 's/$/:/' -e 's/.*:di=\([^:]*\):.*/\1/'
e;f
另一个作弊可能是将 转换:
为换行符,然后它总是匹配a=foo
而没有任何:
tr : '\012' | sed -n 's/^di=//p'
答案3
Posixly,可以如图所示完成。将所有冒号音译为换行符,然后连续砍掉前导 KV 对,直到出现 di= 为止。
{
echo "di=a;b:*.di=c;d:ddi=e;f"
echo "ddi=a;b:di=c;d:*.di=e;f"
echo "*.di=a;b:ddi=c;d:di=e;f"
} \
| sed -n 'y/:/\n/;/^di=/!D;P'
di=a;b
di=c;d
di=e;f
答案4
使用awk
代替sed
、:
和=
作为字段分隔符,遍历每条记录并在找到以下字段时打印下一个字段di
:
$ awk -F '[=:]' '{ for (i = 1; i < NF; ++i) if ($i == "di") { print $(i+1); next } }' file
a;b
c;d
e;f
类似地,但使用:
,=
和换行符作为记录分隔符:
$ awk -v RS='[=:\n]' '$0 == "di" { getline; print }' file
a;b
c;d
e;f
awk
仅当您将多字符值RS
视为正则表达式时,这才有效。最后一个变体也会打印每个 di
如果有多个这样的值,则每个原始行上的值(第一个变体通过调用避免这种情况next
)。