Shell UNIX:使用通配符的 grep

3

我无法理解为什么使用grep时,通配符 * 在以下示例中的解释方式不同:

find . -type f -name \*

结果:

./tgt/etc/test_file.c
./tgt/etc/speleo/test_file.c
./tgt/etc/other_file.c
./src/file.c

我希望您能从此命令中返回与模式匹配的文件,最好带有通配符*。但是:
find . -type f -name \* | grep "tgt/etc/*" # this one works
find . -type f -name \* | grep tgt/etc/* # not this one
find . -type f -name \* | grep tgt/et*/s* # this one works
find . -type f -name \* | grep "tgt/et*/s*" # not this one

我希望有一个实现可以适用于两种情况。我应该使用什么?

5
你明白在正则表达式中 * 和文件名通配符中的作用是不同的,对吧? - Barmar
从这个问题来看,我猜他并没有 :-) - zmo
@Barmar 我可能漏掉了什么,但不明白那与问题有何关联。问题是 [tag:grep] 如何处理参数,也就是说 grep file1 file2 将匹配到 file1file2 两者。 - Reinstate Monica Please
是的,我在发表那条评论后意识到这个问题更加复杂;请看我的答案。 - Barmar
这就是他未加引号的grep的问题,我的评论更适用于他加了引号的grep,因为他们没有正确使用* - Barmar
你说得对,我误解了在shell中作为通配符和正则表达式Quantifier之间的不同用法。或者至少,我没有考虑到这一点。你们的答案使我能够通过使用完整的正则表达式来解决我的问题:例如grep“tgt / et。 / s。*”。谢谢。 - Victor
3个回答

5
grep的第一个参数不是通配符,而是正则表达式。在正则表达式中,*表示匹配其前面的任意数量的字符或表达式。因此,
grep "tgt/etc/*"

匹配 tgt/etc 后面跟着零个或多个 / 字符。在通配符中,* 表示匹配任意数量的任意字符,相当于正则表达式中的 .*。对于您的目的,您需要使用以下命令:

find . -type f -name \* | grep "tgt/etc/"
find . -type f -name \* | grep "tgt/et.*/s"

此外,如果您不引用参数并且其包含任何*字符,则shell将在将其作为参数传递给grep之前将该参数扩展为文件名通配符。因此,当您编写以下内容时:
find . -type f -name \* | grep tgt/etc/*

Shell 会将其扩展为:

find . -type f -name \* | grep tgt/etc/file1 tgt/etc/file2 tgt/etc/file3

这将把tgt/etc/file1视为正则表达式进行搜索,并在剩余的文件中查找它 - 它不会处理管道输入,因为它已经给出了文件名参数。


3
未加引号的示例(不带")在grep看到它们之前,会被shell扩展。这就是Unix shell的工作方式。
对于带引号的示例,请注意,*在grep模式中的含义与它在shell和find中的含义不同:它表示“重复前面的字符任意次数(包括零次)”。

1

这是因为grep使用正则表达式而不是通配符。

所以基本上,你要做的就是:

  • tgt/etc/* 检查零个或多个 /
  • tgt/et*/s* 检查零个或多个 t 和零个或多个 s

但问题是,如果你没有在正则表达式周围加引号,shell会将*扩展为通配符,这会破坏grep看到的正则表达式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接