有没有一种方法告诉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最多可以记住9个模式,但是您需要使用转义括号来记住正则表达式的部分内容。

请看这里的例子和更多细节

运行(s)位数

这个答案适用于任何计数的数字组。例子:

$ echo 'Num123that456are7899900contained0018166intext' \
   | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'

123 456 7899900 0018166

扩大的答案。

有没有办法告诉sed只输出捕获的组?

是的。用捕获组替换所有文本:

$ echo 'Number 123 inside text' \
   | sed 's/[^0-9]*\([0-9]\{1,\}\)[^0-9]*/\1/'

123
s/[^0-9]*                           # several non-digits
         \([0-9]\{1,\}\)            # followed by one or more digits
                        [^0-9]*     # and followed by more non-digits.
                               /\1/ # gets replaced only by the digits.

或者使用扩展语法(减少反引号并允许使用+):

$ echo 'Number 123 in text' \
   | sed -E 's/[^0-9]*([0-9]+)[^0-9]*/\1/'

123

为避免在没有数字时打印原文,请使用:

$ echo 'Number xxx in text' \
   | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'

(-n)默认情况下不打印输入。 (/p)只在替换完成时打印。

并匹配几个数字(也打印它们):

$ echo 'N 123 in 456 text' \
  | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1 /gp'

123 456

这适用于任何计数的数字运行:

$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" \
   | sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'

123 456 7899900 0018166

这与grep命令非常相似:

$ str='Test Num(s) 123 456 7899900 contained as0018166df in text'
$ echo "$str" | grep -Po '\d+'
123
456
7899900
0018166

关于\ d

pattern: /([\d]+)/

Sed不识别'\d'(快捷键)语法。上面[0-9]使用的等价ascii并不是完全等价的。唯一的替代解决方案是使用字符类:'[[:digit:]] '。

所选答案使用这样的“字符类”来构建一个解决方案:

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

该解决方案只适用于(确切地)两组数字。

当然,由于答案是在shell中执行的,我们可以定义几个变量来简化这样的答案:

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

但是,正如已经解释过的,使用s/…/…/gp命令更好:

$ str='This is 75577 a sam33ple 123 text and some 987 numbers'
$ d=[[:digit:]]     D=[^[:digit:]]
$ echo "$str" | sed -rn "s/$D*($d+)$D*/\1 /gp"
75577 33 123 987

这将包括重复的数字运行和编写一个短的(er)命令。

您可以使用ripgrep,它似乎也是简单替换的sed替代品,就像这样

rg '(\d+)' -or '$1'

由于两次匹配,ripgrep使用-o或——only matching和-r或——replace两次只输出$1的第一个捕获组(引用以避免shell将其解释为变量)。

Try

sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"

我在cygwin下得到了这个:

$ (echo "asdf"; \
   echo "1234"; \
   echo "asdf1234adsf1234asdf"; \
   echo "1m2m3m4m5m6m7m8m9m0m1m2m3m4m5m6m7m8m9") | \
  sed -n -e "/[0-9]/s/^[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\)[^0-9]*\([0-9]*\).*$/\1 \2 \3 \4 \5 \6 \7 \8 \9/p"

1234
1234 1234
1 2 3 4 5 6 7 8 9
$

你可以使用grep

grep -Eow "[0-9]+" file