grep 的组匹配包含额外字符

grep 的组匹配包含额外字符

我想在 bash 中使用正则表达式提取一些文本,所以我决定尝试以下简单的示例。

echo "abc def ghi" | grep -Po " \K(.*?) "

我原以为会得到 a "def",但令我惊讶的是"def "我得到的是 a (最后有一个额外的空格)。

我有兴趣了解为什么grep最后还包含额外的空间以及如何摆脱它。我知道我可以用另一行对结果进行后处理,但我有兴趣用 grep 解决这个问题。

答案1

简而言之:

\K

导致 grep 保留所有内容事先的到 \K 并且不将其包含在匹配中。这并不影响接下来发生的事情\K()

这可能就足够了:

" \K(.+)(?= )"

哪里(?= )是非捕获组。

或者也许更好:

" \K([^ ]+)(?= )"
" \K(\w+)(?= )"

或类似的。

答案2

执行您尝试执行的操作的 BREsed可能如下所示:

sed 's/ *\(\([^ ]*\) *\)\{[num]\}.*/\2/'

...或者作为那些支持它的 ERE,sed例如 GNU 和 BSD 版本:

sed -E 's/ *(([^ ]*) *){[num]}.*/\2/p'

[num]...任一表达式都将从第组的第一个字符开始匹配(其中[num]是正整数)模式空间中的非[^ ]*空格字符并继续匹配直到行尾。

但重要的是,它对一些匹配进行了分组:

  • (([^ ]*) *){[num]}- 该组与非空格组和任何/所有后续空格字符的出现次数一样多[num],并且可以作为 进行反向引用\1
    • {[num]}- 当一个模式被匹配\{[num]\}多次时,对它的唯一引用是最后一个 - 因此即使该组匹配指定的模式的多次出现,它返回的唯一引用也是最后一个。
  • ([^ ]*)- 但是,上述组的子组仅匹配 中匹配的非空格字符的子集\1。该子组可以在 中引用\2
  • *并且.*- 这匹配模式空间前导的任何/所有空格字符以及子表达式中匹配的出现后的任何/所有字符。
  • /\2/- 这仅用 中引用的组替换上述所有内容\2

因为[^ ]**是布尔补码,并且[^ ]*U*一起可以描述任何可能的字符串,所以上述正则表达式通用。

对于你的例子:

for n in 1 2 3 4
do  echo "abc def ghi" | 
    sed -E "s/ *(([^ ]*) *){$n}.*/\2/"
done | sed -n l

...印刷...

abc$
def$
ghi$
$

照原样,它总是会为上面要求的指定事件打印一个空行,但是 - 如果不希望这样做 - 可以从输出中完全删除该行,如下所示:

sed -En 's/ *(([^ ]*) *){[num]}.*/\2/;/./p'

更进一步,可以在全局范围内应用替换以仅获得每一次[num]出现的情况。由于*相当有限,我将用它来[[:space:]]*代替 - 它将匹配任何<space><tab><newline><vertical tab><return>.

s=
{   printf "${s:=$(printf '\r\v\t%10s')}"
    seq -s"$s" 100
} | sed -En "s/[${s:=[:space:]}]*(([^$s]*)[$s]*){21}/\2\\
/g;      /[^$s]/s/\n*$//p"

在应用之前,sed上面的printf ...; seq ...位会打印一行,例如:

\r\v\t          1\r\v\t          2\r\v\t          3\r\v\t...

... 等等。但应用上面的内容sed会得到:

21
42
63
84

...打印的数字后面没有空格。

相关内容