鉴于Linux环境中存在许多用于操作字符串的包(grep、awk、sed...),我想要一个软件以类似php/preg的语法提取捕获组。
也许最接近的是,grep -P
但我不明白它是如何工作的。
像这样的东西cat file.txt | grep -P '/something="([\w]+)"/i'
似乎不仅仅给我捕获组内的内容。
有人可以给我提供一些可行的例子吗?请多多支持,并解释一些变体和限制!
编辑:我看到某个地方用于sed
实现此目的,但我对其语法仍然有点困惑。
答案1
pcregrep -io1 'something="(\w+)"' myfile.txt
(-i
对于不区分大小写的匹配,-o1
打印第一个捕获组)。
GNUgrep
支持-P
(如果使用 perl 兼容的正则表达式支持构建) 和-o
.然而,它-o
仅限于打印整个匹配部分。不过,您可以使用 perl 环视运算符来解决这个问题:
grep -iPo '(?<=something=")\w+(?=")' myfile.txt
(即,匹配序列的正则表达式单词组成字符前提是它后面something="
跟着"
)。
或者使用最近的 PCRE:
grep -iPo 'something="\K\w+(?=")' myfile.txt
(\K
其中重置了匹配的细绳)。
但如果您要使用 perl 正则表达式,您也可以使用perl
:
perl -C -lne 'print for /something="(\w+)"/ig' myfile.txt
使用 GNU 或 BSD sed
,仅返回每行最右边的匹配项:
sed -nE 's/.*something="(\w+)".*/\1/pi' myfile.txt
可移植(因为扩展的正则表达式支持和不区分大小写的匹配是非标准扩展,并非所有sed
实现都支持):
sed -n 's/.*[sS][oO][mM][eE][tT][hH][iI][nN][gG]="\([[:alnum:]_]\{1,\}\)".*/\1/p' myfile.txt
假设大写的i
是I
.这意味着,例如在大写字母的区域设置中i
,İ
行为将与以前的解决方案不同。
一个标准/便携式解决方案,可以找到一行中所有出现的情况:
awk '{while(match(tolower($0), /something="[[:alnum:]_]+"/)) {
print substr($0, RSTART+11, RLENGTH-12)
$0 = substr($0, RSTART+RLENGTH-1)}}' myfile.txt
如果输入包含小写版本的长度(字符数)不同的文本,则可能无法正常工作。
陷阱:
\w
所有这些解决方案之间关于( 和[[:alnum:]_]
) 在 C/POSIX 以外的语言环境中匹配的内容都会存在一些差异。无论如何,它至少应包含下划线、所有十进制阿拉伯数字和拉丁英语字母表中的字母(大写和小写)。如果您只需要这些,请将区域设置固定为 C。- 如上所述,不区分大小写的匹配非常依赖于语言环境。如果您只关心
a-z
vsA-Z
英文字母,则可以再次将语言环境修复为 C。 .
至少具有 GNU 实现的正则表达式运算符永远sed
不会匹配不属于有效字符的字节序列。例如,在 UTF-8 语言环境中,这意味着它不会匹配具有第 8 位集的单字节字符集中的字符。或者换句话说,为了使sed
解决方案正常工作,输入文件中使用的字符集必须与用户区域设置中的字符集相同。perl
,pcregrep
并且 GNU 实用程序通常会处理任意长度的行,并包含任意字节值(但请注意上面的警告),并且会将最后一个换行符之后的额外数据视为额外行。这些实用程序的其他实现可能不会。上面的模式依次与输入中的每一行进行匹配。这意味着它们不能匹配多于一行的输入。对于这样的模式来说,这不是问题
something="\w+"
,不能跨越超过一行,但在一般情况下,如果您希望模式匹配可能跨越多行的文本,例如something=".*?"
,那么您需要:- 更改您处理的记录类型。
grep --null
,sed -z
(sed
仅限 GNU),perl -0
,awk -v RS='\0'
(仅限 GNUawk
和最新版本mawk
)可以处理 NUL 分隔记录而不是行(换行分隔记录),GNUawk
可以使用任何正则表达式作为记录分隔符(使用-v RS='regexp'),
perlany byte value (with
-0ooo`)。 pcregrep
有一个-M
多行模式。- 使用 的
perl
slurp 模式,其中整个输入是一条记录(带有-0777
)
然后,对于 perl 和 pcre 的,请注意,
.
除非启用该标志,否则不会匹配换行符s
,例如使用pcregrep -Mio1 '(?s)something="(.*?)"'
或perl -C -l -0777 -ne 'print for /something="(.*?)"/gis'
- 更改您处理的记录类型。
grep
请注意,和的某些版本pcregrep
存在-z
或 的错误-M
,并且正则表达式引擎通常对它们可能投入的匹配正则表达式的工作量有一些内置限制。
答案2
在 Linux 上,您有多个命令,每个命令都有不同的功能。 - 您的工作是为给定的工作找到合适的工具。 ;)
您并没有真正指定具体问题,所以我需要保持笼统。
也许最简单的方法是直接使用 perl:
cat file.txt | perl -wne '/([\w]+)/i and print $1'
另请阅读man grep
grep 的一些选项:
-o, --only-matching
Print only the matched (non-empty) parts of a matching line, with each such part on a separate output line.
您可以使用例如:
cat file.txt | grep -o '\w*'
但什么是最好的实际上取决于您的问题。如果您喜欢 php,实际上甚至可以从命令行使用 php。
答案3
这是基于 的另一个答案perl
,这个答案使用perl -ne
它将输入的所有行提供/消耗到 perl 程序中。
该perl
程序有一条if
语句,其中包含带有捕获组的正则表达式,当我们找到匹配项时,我们会打印它。
当我们打印捕获组时,我们添加一个换行符。换行符对于确保多个匹配项由换行符分隔至关重要,否则,所有结果将在同一行上混合在一起,并可能产生意外/不需要的结果。
如果我们获得与捕获组匹配的多行,大多数时候,我们只对第一个匹配行感兴趣,因此对用法感兴趣head -1
。
以下bash
脚本说明了我们如何使用它来处理输入文件并将提取的结果保存到变量中value
。
cat file.txt # something="nice"
value=$(cat file.txt | perl -ne 'if (/something="([\w]+)"/) { print $1 . "\n" }' | head -1)
echo $value # nice