有没有一种方法告诉sed只输出捕获的组?

例如,给定输入:

This is a sample 123 text and some 987 numbers

和模式:

/([\d]+)/

我能得到只有123和987输出的方式格式化后引用?


当前回答

放弃吧,使用Perl

既然sed不能解决这个问题,就让我们放弃并使用Perl吧,至少它是LSB,而grep GNU扩展不是:-)

Print the entire matching part, no matching groups or lookbehind needed: cat <<EOS | perl -lane 'print m/\d+/g' a1 b2 a34 b56 EOS Output: 12 3456 Single match per line, often structured data fields: cat <<EOS | perl -lape 's/.*?a(\d+).*/$1/g' a1 b2 a34 b56 EOS Output: 1 34 With lookbehind: cat <<EOS | perl -lane 'print m/(?<=a)(\d+)/' a1 b2 a34 b56 EOS Multiple fields: cat <<EOS | perl -lape 's/.*?a(\d+).*?b(\d+).*/$1 $2/g' a1 c0 b2 c0 a34 c0 b56 c0 EOS Output: 1 2 34 56 Multiple matches per line, often unstructured data: cat <<EOS | perl -lape 's/.*?a(\d+)|.*/$1 /g' a1 b2 a34 b56 a78 b90 EOS Output: 1 34 78 With lookbehind: cat EOS<< | perl -lane 'print m/(?<=a)(\d+)/g' a1 b2 a34 b56 a78 b90 EOS Output: 1 3478

其他回答

让它工作的关键是告诉sed排除您不想输出的内容,并指定您想要输出的内容。这个技巧取决于你知道你要找多少匹配。下面的grep命令适用于不指定数量的匹配。

string='This is a sample 123 text and some 987 numbers'
echo "$string" | sed -rn 's/[^[:digit:]]*([[:digit:]]+)[^[:digit:]]+([[:digit:]]+)[^[:digit:]]*/\1 \2/p'

这表示:

不要默认打印每行(-n) 排除零或多个非数字 包含一个或多个数字 排除一个或多个非数字 包含一个或多个数字 排除零或多个非数字 打印替换(p)(在一行上)

一般来说,在sed中使用括号捕获组,并使用反向引用输出捕获的组:

echo "foobarbaz" | sed 's/^foo\(.*\)baz$/\1/'

将输出“bar”。如果你使用-r (-E用于OS X)扩展正则表达式,你不需要转义括号:

echo "foobarbaz" | sed -r 's/^foo(.*)baz$/\1/'

最多可以有9个捕获组及其反向引用。反向引用按照组出现的顺序编号,但它们可以以任何顺序使用,并且可以重复使用:

echo "foobarbaz" | sed -r 's/^foo(.*)b(.)z$/\2 \1 \2/'

输出“a bar a”。

如果你有GNU grep:

echo "$string" | grep -Po '\d+'

它也可以在BSD中工作,包括OS X:

echo "$string" | grep -Eo '\d+'

这些命令将匹配任意数量的数字序列。输出将在多行上。

或者像这样的变化:

echo "$string" | grep -Po '(?<=\D )(\d+)'

-P选项启用Perl兼容正则表达式。参见man 3 pcrepattern或man 3 pcresyntax。

您需要包含整行来打印组,这是您在第二个命令中所做的,但您不需要对第一个通配符进行分组。这也可以:

echo "/home/me/myfile-99" | sed -r 's/.*myfile-(.*)$/\1/'

我相信问题中给出的模式只是举例,目标是匹配任何模式。

如果你有一个允许在模式空间中插入换行符的GNU扩展sed,一个建议是:

> set string = "This is a sample 123 text and some 987 numbers"
>
> set pattern = "[0-9][0-9]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
123
987
> set pattern = "[a-z][a-z]*"
> echo $string | sed "s/$pattern/\n&\n/g" | sed -n "/$pattern/p"
his
is
a
sample
text
and
some
numbers

这些例子是用tcsh(是的,我知道它是错误的shell)和CYGWIN。(编辑:对于bash,删除set和=周围的空格。)

放弃吧,使用Perl

既然sed不能解决这个问题,就让我们放弃并使用Perl吧,至少它是LSB,而grep GNU扩展不是:-)

Print the entire matching part, no matching groups or lookbehind needed: cat <<EOS | perl -lane 'print m/\d+/g' a1 b2 a34 b56 EOS Output: 12 3456 Single match per line, often structured data fields: cat <<EOS | perl -lape 's/.*?a(\d+).*/$1/g' a1 b2 a34 b56 EOS Output: 1 34 With lookbehind: cat <<EOS | perl -lane 'print m/(?<=a)(\d+)/' a1 b2 a34 b56 EOS Multiple fields: cat <<EOS | perl -lape 's/.*?a(\d+).*?b(\d+).*/$1 $2/g' a1 c0 b2 c0 a34 c0 b56 c0 EOS Output: 1 2 34 56 Multiple matches per line, often unstructured data: cat <<EOS | perl -lape 's/.*?a(\d+)|.*/$1 /g' a1 b2 a34 b56 a78 b90 EOS Output: 1 34 78 With lookbehind: cat EOS<< | perl -lane 'print m/(?<=a)(\d+)/g' a1 b2 a34 b56 a78 b90 EOS Output: 1 3478

这不是OP要求的(捕获组),但你可以使用以下方法提取数字:

S='This is a sample 123 text and some 987 numbers'
echo "$S" | sed 's/ /\n/g' | sed -r '/([0-9]+)/ !d'

给出以下内容:

123
987