sed 匹配行首或字符

sed 匹配行首或字符

我有一串用冒号分隔的作业,每个作业的形式都是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)。

相关内容