我想在 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
...打印的数字后面没有空格。