如何仅使用sed输出捕获的组?

Pab*_*blo 258 regex sed

有没有办法告诉sed输出只捕获的组?例如,给定输入:

This is a sample 123 text and some 987 numbers
Run Code Online (Sandbox Code Playgroud)

和模式:

/([\d]+)/
Run Code Online (Sandbox Code Playgroud)

我可以通过反向引用格式化的方式获得123和987输出吗?

Pau*_*ce. 308

让它工作的关键是告诉sed您排除不想输出的内容以及指定您想要的内容.

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

这说:

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

通常,在sed使用括号捕获组并使用后引用输出捕获的组:

echo "foobarbaz" | sed 's/^foo\(.*\)baz$/\1/'
Run Code Online (Sandbox Code Playgroud)

将输出"bar".如果使用-r(-E对于OS X)扩展正则表达式,则无需转义括号:

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

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

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

输出"a bar a".

如果你有GNU grep(它也可以在BSD中工作,包括OS X):

echo "$string" | grep -Po '\d+'
Run Code Online (Sandbox Code Playgroud)

或变化,如:

echo "$string" | grep -Po '(?<=\D )(\d+)'
Run Code Online (Sandbox Code Playgroud)

-P选项启用Perl兼容正则表达式.见man 3 pcrepatternman 3 pcresyntax.

  • 需要注意的是,OSX Mountain Lion不再支持grep中的PCRE. (22认同)
  • 请你的系统管理员安装gsed.你会惊讶于几个甜甜圈能给你带来什么...... (7认同)
  • @lumbric:如果您指的是`sed`示例,如果您使用`-r`选项(或OS-II为`-E`,IIRC),则无需转义括号.区别在于基本正则表达式和扩展正则表达式(`-r`)之间. (7认同)
  • 请注意,您可能需要在'('和')'前加上'\',我不知道为什么. (3认同)
  • 我发现接受的答案令人困惑,因为它在示例中合并了一个大的正则表达式,使得提取所需的信息变得困难:在 sed 中,您必须转义括号“\(.*\)”,使用“\1”访问捕获组、`\2` 等.. (2认同)

Pet*_*McG 52

Sed最多有九种记忆模式,但你需要使用转义括号来记住正则表达式的部分内容.

请参阅此处以获取示例和更多详细信

  • `sed -e's/version = \(.+ \)/\1 /'input.txt`这仍然会输出整个input.txt (55认同)
  • @Pablo的评论应该是公认的答案 (4认同)
  • 我建议使用 `sed -E` 来使用所谓的“现代”或“扩展”正则表达式,它们看起来更接近 Perl/Java/JavaScript/Go/任何风格。(与`grep -E` 或`egrep` 相比。)默认语法有那些奇怪的转义规则,被认为是“过时的”。有关两者之间差异的更多信息,请运行“man 7 re_format”。 (3认同)
  • @Pablo,在你的模式中,你必须写 `\+` 而不是 `+`。我不明白为什么人们只将 `-e` 用于一个 sed 命令。 (2认同)

gho*_*g74 30

你可以用grep

grep -Eow "[0-9]+" file
Run Code Online (Sandbox Code Playgroud)

  • @Bert F:我理解匹配的部分,但它不是捕获组.我想要的是这样([0-9] +).+([abc] {2,3})所以有2个捕获组.我想通过反向引用或其他方式输出捕获组. (13认同)
  • @ ghostdog74:绝对同意你的看法.如何才能获得仅输出捕获组的greo? (4认同)
  • @Michael - 这就是为什么有 `o` 选项 - http://unixhelp.ed.ac.uk/CGI/man-cgi?grep : -o, --only-matching 只显示匹配行的一部分匹配模式 (2认同)
  • @Pablo:grep 只输出匹配的内容。要给它多个组,请使用多个表达式: `grep -Eow -e "[0-9]+" -e "[abc]{2,3}"` 我不知道如何要求这两个表达式除了来自先前 grep 的管道之外,位于一行上(如果任一模式在一行上匹配多次,则仍然无法工作)。 (2认同)

Jos*_*sey 9

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

如果你有一个带有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
Run Code Online (Sandbox Code Playgroud)

这些例子与CYGWIN的tcsh(是的,我知道它是错误的shell)有关.(编辑:对于bash,删除set,以及=周围的空格.)

  • @RandomInsano:为了使用`+`,你需要转义它或使用`-r`选项(OS-X的`-E`).您也可以使用`\ {1,\}`(或`-r`或`-E`而不进行转义). (4认同)
  • 只是一个注释,但加号“ +”表示“一个或多个”,这消除了在模式中重复自己的需要。因此,“ [0-9] [0-9] *”将变为“ [0-9] +” (2认同)

Cir*_*四事件 9

放弃并使用Perl

既然sed不削减它,让我们只是扔掉毛巾并使用Perl,至少它是LSBgrepGNU扩展不是:-)


ImH*_*ere 9

运行数字

此答案适用于任何数字组计数.例:

$ echo 'Num123that456are7899900contained0018166intext' |
> sed -En 's/[^0-9]*([0-9]{1,})[^0-9]*/\1 /gp'
123 456 7899900 0018166
Run Code Online (Sandbox Code Playgroud)

扩大答案.

有没有办法告诉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.
Run Code Online (Sandbox Code Playgroud)

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

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

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

$ echo 'Number xxx in text' | sed -En 's/[^0-9]*([0-9]+)[^0-9]*/\1/p'
Run Code Online (Sandbox Code Playgroud)
  • (-n)默认情况下不打印输入.
  • (/ p)仅在更换时打印.

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

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

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

$ 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
Run Code Online (Sandbox Code Playgroud)

这与grep命令非常相似:

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

关于\ d

和模式: /([\d]+)/

Sed无法识别'\ d'(快捷方式)语法.上面使用的ascii等价物[0-9]并不完全相同.唯一的替代解决方案是使用字符类:'[[: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'
Run Code Online (Sandbox Code Playgroud)

该解决方案仅适用于(确切)两个数字运行.

当然,由于答案是在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"
Run Code Online (Sandbox Code Playgroud)

但是,正如已经解释的那样,使用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
Run Code Online (Sandbox Code Playgroud)

这将涵盖重复的数字运行和编写短(呃)命令.


Ber*_*t F 5

尝试

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"
Run Code Online (Sandbox Code Playgroud)

我在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
$
Run Code Online (Sandbox Code Playgroud)


小智 5

您需要包含整行来打印组,这是您在第二个命令中执行的操作,但不需要对第一个通配符进行分组。这也将起作用:

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