从Grep RegEx中捕获分组

12 浏览
0 Comments

从Grep RegEx中捕获分组

我有一个在sh(macOS 10.6)中的脚本,用于查找一个文件数组:\n

files="*.jpg"
for f in $files
    do
        echo $f | grep -oEi '[0-9]+_([a-z]+)_[0-9a-z]*'
        name=$?
        echo $name
    done

\n到目前为止,$name只是简单地保存0、1或2,这取决于grep是否发现文件名与提供的模式匹配。我希望能够捕获圆括号([a-z]+)中的内容,并将其存储到一个变量中。\n如果可能的话,我希望只使用grep。如果不行,请不要使用Python或Perl等语言。我希望从*nix纯粹主义的角度来解决这个问题,可以使用sed或类似的工具。

0
0 Comments

从上面的内容可以看出,这篇文章讨论了在使用grep工具时的一个问题,即如何从匹配的结果中提取出捕获组的内容。原帖中的解决方法是使用pcregrep工具替代grep,并且通过在-o参数后添加数字参数来指定要显示的捕获组。pcregrep工具是grep的一个扩展,具有相同的语法,但实现了提取捕获组的功能。此解决方法只需要对脚本进行最小的更改,只需要将一个模块化的实用工具替换为另一个,并调整参数即可。

此外,还提到了pcregrep工具在某些操作系统上不可用的问题,以及一些用户对pcregrep工具的版本和功能有疑问的讨论。同时还介绍了一些pcregrep工具的其他功能和用法。

总结起来,这篇文章讨论了从grep结果中提取捕获组的问题,给出了使用pcregrep工具替代grep的解决方法,并介绍了pcregrep工具的一些其他功能和用法。

0
0 Comments

问题出现的原因:纯粹的grep命令通常不能直接捕获组,至少在一般情况下是不行的。但是,如果你的模式适用,你可以在管道中多次使用grep命令,先将行减少到已知格式,然后提取你想要的部分。虽然像cut和sed这样的工具在这方面做得更好。

解决方法:假设为了论证而言,你的模式要简单一些:[0-9]+_([a-z]+)_。你可以这样提取:

echo $name | grep -Ei '[0-9]+_[a-z]+_' | grep -oEi '[a-z]+'

第一个grep命令会删除任何不符合整体模式的行,第二个grep命令(指定了--only-matching选项)会显示名称的字母部分。这仅在模式合适的情况下起作用: "alpha portion"足够具体以提取出你想要的内容。

(顺便说一句:我个人会使用grep + cut来实现你想要的效果:echo $name | grep {pattern} | cut -d _ -f 2。这样会让cut按照分隔符_将行解析为字段,并只返回第二个字段(字段编号从1开始))。

Unix哲学是拥有做一件事并做好的工具,并将它们组合起来实现非平凡的任务,因此我认为grep + sed等更符合Unix方式的做事方式。

对于你的问题,你可以这样写:

for f in $files; do name=echo $f | grep -oEi '[0-9]+_([a-z]+)_[0-9a-z]*'| cut -d _ -f 2; Aha!

我不同意那种"哲学"。如果你可以使用shell的内置功能而不调用外部命令,那么你的脚本在性能上会更快。有一些功能重叠的工具,例如grep、sed和awk。它们都可以进行字符串操作,但awk在其中表现得更出色,因为它可以做更多。实际上,所有这些命令的链接,比如上面的双重grep或grep + sed,可以通过一个awk进程来完成缩短。

回复:我同意将许多小操作链接在一起通常比在一个地方完成所有操作的效率要低,但是我仍然坚持认为Unix哲学是让许多工具协同工作。例如,tar只能归档文件,不能压缩文件,并且因为默认情况下输出到STDOUT,所以你可以通过网络使用netcat将其传输,或者使用bzip2进行压缩,等等。在我看来,这强调了Unix工具应该能够在管道中协同工作的惯例和总体精神。

cut太棒了-感谢你的提示!至于工具与效率的争论,我喜欢链接工具的简单性。

为grep的-o选项点赞,非常有用。

0
0 Comments

最近有个问题讨论了如何从一个正则表达式中获取捕获组。问题的背景是在Bash中使用grep命令时,如何获取匹配到的字符串的一部分。原始的代码如下:
files="*.jpg"
regex="[0-9]+_([a-z]+)_[0-9a-z]*"
for f in $files
do
if [[ $f =~ $regex ]]
then
name="${BASH_REMATCH[1]}"
echo "${name}.jpg"
name="${name}.jpg"
else
echo "$f doesn't match" >&2
fi
done
[/apcode]

首先要注意的是,将正则表达式放在一个变量中会更好,因为某些模式如果直接包含在代码中可能无法正常工作。这里使用了Bash的正则表达式匹配运算符`=~`,匹配的结果会保存在`$BASH_REMATCH`数组中,第一个捕获组保存在索引1中,第二个捕获组(如果有的话)保存在索引2中,以此类推。索引0保存的是完整的匹配结果。

需要注意的是,由于没有使用锚点,这个正则表达式(以及使用grep的那个正则表达式)将匹配以下示例以及其他更多的字符串,这可能不是你想要的:

123_abc_d4e5
xyz123_abc_d4e5
123_abc_d4e5.xyz
xyz123_abc_d4e5.xyz

为了排除第二个和第四个示例,可以将正则表达式修改为:

^[0-9]+_([a-z]+)_[0-9a-z]*

这个正则表达式表示字符串必须以一个或多个数字开头。插入符号`^`表示字符串的开头。如果在正则表达式的末尾加上一个美元符号,如下所示:

^[0-9]+_([a-z]+)_[0-9a-z]*$

那么第三个示例也会被排除,因为点号不在正则表达式中。需要注意的是,第四个示例也无法匹配这个正则表达式。

如果你使用的是GNU的grep(大约在2.5版本之后,添加了`\K`运算符):

name=$(echo "$f" | grep -Po '(?i)[0-9]+_\K[a-z]+(?=_[0-9a-z]*)').jpg

`\K`运算符表示前面的模式将匹配成功,但不包含在结果中。等价的固定长度运算符是`(?<=)`,它将包括在右括号之前。如果量词可能匹配不同长度的字符串(例如`+`、`*`、`{2,4}`等),则必须使用`\K`运算符。

`(?=)`运算符匹配固定或变长的模式,它被称为“向前查找”。它也不会包含在结果中。

为了使匹配不区分大小写,可以使用`(?i)`运算符。它会影响其后的模式,所以它的位置很重要。

根据文件名中是否有其他字符,可能需要调整正则表达式。需要注意的是,在这个例子中,我展示了在捕获子字符串的同时将字符串连接在一起的示例。

在这个回答中,我想点赞那句“最好将正则表达式放在一个变量中。某些模式如果直接包含在代码中可能无法正常工作。”

“最好将正则表达式放在一个变量中。某些模式如果直接包含在代码中可能无法正常工作。” - 为什么会出现这种情况?有没有办法修复它们?

原因是某些模式中包含了空白字符。由于需要对这些字符进行转义,因此使用引号可能会导致将正则表达式从正则表达式转换为普通字符串。正确的做法是使用变量。在赋值过程中可以使用引号,这样会简化很多事情。

然而,Bash的正则表达式不支持惰性匹配。

`\K`运算符可以在某些情况下非常有用,比如在目录操作中,你只需要一个快速的一行命令。例如,我遇到的情况是在一个文件中查找一个名称,并根据结果创建一些目录。

这样的做法实际上是行不通的。无论正则表达式或输入字符串是什么,都无法匹配。

这是行得通的。你使用的是哪个版本的Bash?告诉我你做了什么导致它不起作用,也许我可以告诉你原因。

我使用的是`4.3.11(1)-release`版本。我完全按照示例的方式复制了代码。`echo "${name}.jpg"`输出的是".jpg"。

你当前目录中是否有与模式匹配的文件?例如,`touch 012_abc_03a.jpg 345_def_14b.jpg`会创建两个空的测试文件,这个正则表达式可以匹配到。在我的回答中,正则表达式的匹配和输出应该是在一个`if`语句中,而不是单独出现,以避免输出空结果。我会做出这个改变以提高清晰度。

我没有使用文件,我改编了代码以适应我的场景,即从提交的svn日志中提取文本。我有正则表达式,使用grep命令可以匹配,但使用`=~`不能匹配。

如果没有具体的示例,我无法提供帮助。也许你应该将这个问题单独发布出来。

它是可以工作的,示例很容易理解。实际上,这是一个很好的回答。你肯定是做错了什么。

问题是关于GREP的,而不是BASH。

我的回答中包含了关于grep的信息。而且它也被提问者接受并且得到了很多赞同。感谢你的负评。

你几乎肯定是写错了什么。哪个命令显示未找到?你使用的是哪个版本的Bash?

例如,`regex="([A-z]+)\."`,`"foo.bar"=~$regex`,`-bash: foo.bar=~([A-z]+)\.: command not found`。

在`=~`的两侧需要添加空格,并且这个表达式需要包含在双括号中。在花括号展开中,不需要转义字符(点号始终是字面字符,这是一个通配符,不是正则表达式)。

我两种方式都尝试了。`"foo.bar" =~ $regex`仍然显示`-bash: foo.bar: command not found`。`[[ "foo.bar" =~ $regex ]]`也显示`-bash: [[foo.bar: command not found`。谢谢你提醒多余的转义字符。

你还需要在双括号内部添加空格,就像我的回答中展示的那样。

啊!我以为这些双大括号是if语句的括号,因为我在我的用例中没有if语句,所以没有意识到我需要它们。谢谢!

关于grep和`\K`运算符的部分,在使用sh而不是bash的情况下非常有用,例如在buildroot软件包的Makefile中。

在这种情况下,BASH_REMATCH会导致“missing separator”错误,并且你可能不知道为什么,直到意识到构建系统可能正在使用sh。

以上就是从问题的出现原因以及解决方法中整理出来的内容。希望对你有所帮助。

0