4

我一直在尝试使用andsys.argv[1]递归地获取与命令行参数 ()中的 glob 模式匹配的文件列表。问题是,bash(和许多其他 shell)自动将 glob 模式扩展为文件名。glob.globos.walk

那么标准的unix程序(例如grep -R)如何做到这一点?我意识到它们不在 python 中,但如果这发生在 shell 级别,那应该没关系,对吧?脚本有没有办法告诉 shell 不自动扩展全局模式?看起来set -f会禁用通配符,但我不确定如何足够早地运行它,可以这么说。

我见过使用 Glob() 在 Python 中递归查找文件?,但这并不包括从命令行参数中实际获取 glob 模式。

谢谢!

编辑:

类似 grep 的 perl 脚本ack接受 perl 正则表达式作为其参数之一。因此,ack .*打印出每个文件的每一行。但.*应该扩展到目录中的所有隐藏文件。我尝试阅读脚本,但我不知道 perl;它怎么能做到这一点?

4

3 回答 3

6

shell 在考虑调用命令之前执行 glob 扩展。诸如 grep 之类的程序不会做任何事情来防止通配:它们不能。作为这些程序的调用者,你必须告诉 shell 你想将特殊字符如*?传递给程序,而不是让 shell 解释它们。您可以通过将它们放在引号内来做到这一点:

grep -E 'ba(na)* split' *.txt

(在所有名为 <something> 的文件中查找ba split,bana split.txt)在这种情况下,单引号或双引号都可以解决问题。在单引号之间,shell 什么也不扩展。在双引号之间,$,`\仍然被解释。您还可以通过在单个字符前面加上反斜杠来保护单个字符免受 shell 扩展。不仅需要保护通配符;例如,在上面,模式中的空格用引号引起来,因此它是参数的一部分,grep而不是参数分隔符。编写上述代码段的替代方法包括

grep -E "ba(na)* split" *.txt
grep -E ba\(na\)\*\ split *.txt

对于大多数 shell,如果参数包含通配符但模式与任何文件都不匹配,则模式保持不变并传递给底层命令。所以像这样的命令

grep b[an]*a *.txt

根据系统上存在的文件,具有不同的效果。如果当前目录不包含任何名称以 开头的文件b,则该命令b[an]*a在名称匹配的文件中搜索模式*.txt。如果当前目录包含名为baclava,bnm和的文件hello.txt,则该命令扩展为grep baclava bnm hello.txt,因此它baclava在两个文件bnm和中搜索模式hello.txt。不用说,在脚本中依赖它是个坏主意。在命令行上它偶尔可以节省打字,但这是有风险的。

当您ack .*在不包含点文件的目录中运行时,shell 会运行ack . ... 然后该ack命令的行为是递归地打印出(当前目录的父目录)下所有文件中的所有非空行(模式.:匹配任何一个字符) 。..与 相比,它在当前目录及其子目录ack '.*'中搜索模式(匹配任何内容)(由于不传递任何文件名参数时的行为)。.*ack

于 2011-05-24T10:08:09.813 回答
1

当涉及到 grep 时,它只接受文件名列表,并且本身不进行 glob 扩展。如果您确实需要将模式作为参数传递,则必须在命令行中用单引号将其引用。但在你这样做之前,请考虑让 shell 完成它设计的工作。

于 2011-05-23T22:29:10.570 回答
1

是的set -f,你在正确的轨道上。

听起来你要从 shell 调用你的 python 程序。

每当您使用 shell 发出命令时,它都会尝试扫描命令行并处理通配符、命令替换和一大堆其他事情。

因此,在命令行上运行程序之前,您必须关闭 globing

set -f
echo *
*

myprogram *.txt

会将字符串 '*.txt' 传递给您的程序。然后您可以使用内部通配符来获取您的文件。

或者你可以通过创建一个包装脚本来做同样的事情

 #!/bin/bash
 set -f
 myProgram ${@}

其中${@} are the arguments you pass in when you startmyProgram 来自命令行、crontab 或来自另一个进程的 exec(...)。

我希望这有帮助。

于 2011-05-23T22:30:27.977 回答