我想使用通配符“*”来 grep 所有 .txt 文件。
我尝试了这个命令(以及没有引号“”)但失败了。
ls | grep "*.txt"
有趣的是,如果我在 grep 命令中放入与目录中的 .txt 文件相对应的另一个字符,它就会起作用
>>ls | grep s*.txt
sample.txt
我知道这ls *.txt
会起作用,但我对 grep 命令的性质有点惊讶。有人可以帮助我为什么会发生这种情况吗?
是不是因为grep用了正则表达式,请帮忙。
答案1
在正则表达式中,*
意味着“任意数量的前一项”,而不是“任意数量的任意字符”,就像在 shell 模式中那样。并.
表示“任何单个字符”。因此,要查找“任何内容,后跟文字.txt
”,您可以使用.*\.txt
.或者只是\.txt
,因为通常正则表达式匹配会在行中的任何位置搜索匹配项。然后,\.txt
还会匹配类似 的文件名foo.txtgz
,因为.txt
不必位于末尾。您需要\.txt$
将模式锁定到行尾。
正则表达式*.txt
要么是无意义的、是错误的,要么是查找字面星号,具体取决于实现以及您使用的是基本正则表达式 ( grep
) 还是扩展正则表达式 ( grep -E
)。最好不要使用它。
另一方面,s*.txt
将查找“任意数量的字母s
,然后是任何单个字符,然后是文字txt
”。这是一个更有效的正则表达式,但是...仍然不匹配sample.txt
。
相反,第二个命令中发生的情况是,由于s*.txt
未加引号,因此 shell 会在看到它s*.txt
之前展开grep
它。如果唯一匹配的文件是sample.txt
,则grep
在 的输出中查找该文件ls
。 (如果有多个匹配的文件名,第一个将被视为模式,其余的将作为文件名进行grep
读取。在这种情况下,它将忽略来自管道的输入。)
但是,ls
也可以获取文件列表,因此虽然您可以使用
ls | grep '\.txt'
要获取任何.txt
文件,使用它可能会更容易
ls *.txt
反而。
答案2
部分原因是因为grep
使用了正则表达式(事实上,这就是re
名称中的代表的意思 - 它是G全局的r规则的e表达p打印)。
正则表达式中的通配符与shell 通配符中的通配符*
不同。*
在正则表达式中,*
表示“零个或多个先前定义的对象”。然而,.
是还通配符,意思是“一个字符”。
在 shell glob 中,*
表示“零个或多个字符”。 .
根本不是通配符。
当您grep
查找模式时"*.txt"
,您正在寻找零个或多个任何内容,后跟一个字符,最后是文字字符串txt
。
当您grep
使用模式"s*.txt"m you are looking for a literal
s , followed by zero or more
s s, followed by any character, followed by the literal string
txt`.
这就是为什么您在正则表达式中会发现的一个常见现象是.*
,这意味着“任何字符之一后跟零个或多个任何字符”。正则表达式“实际上是除零字符之外的任何字符组合”。
当您ls *.txt
告诉 shell“查找与 glob 模式匹配的任何文件名”时*.txt
,请在此处列出它们,并将它们作为参数提供给ls
命令。
答案3
请注意 grep 正在搜索文件内容第一个参数是搜索模式,其他参数解释为要查看的文件
使用grep -H -o
标志或将其放入grep
脚本中并运行它以bash -x script
查看 shell globs 在作为参数传递之前如何扩展时,它会变得更加清晰