【问题标题】:Recursively matching filenames with glob argument使用 glob 参数递归匹配文件名
【发布时间】:2011-08-31 12:23:51
【问题描述】:

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

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

我见过 Use a Glob() to find files recursively in Python?,但这并没有涵盖从命令行参数中实际获取 glob 模式。

谢谢!

编辑:

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

【问题讨论】:

    标签: python bash glob argv


    【解决方案1】:

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

    【讨论】:

    • 啊,我明白了,这是一个很好的观点。多年来一直使用 grep 并且从未注意到它实际上并没有对类似 glob 的模式做任何事情(对于其他 unix 命令也是如此)。嗯,谢谢!
    • 这符合 Unix 哲学,即每个工具都应该有单独的责任。
    【解决方案2】:

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

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

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

    所以你必须在在命令行上运行程序之前关闭通配符

    set -f
    echo *
    *
    
    myprogram *.txt
    

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

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

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

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

    我希望这会有所帮助。

    【讨论】:

    • 你的意思是先在shell显式运行set -f,然后再运行程序吗?我认为将 python 程序包装在一个首先调用 set -f 的 bash 脚本中是行不通的……嗯,谢谢!
    • 刚刚试过了,唉,程序仍然得到扩展的文件名。当我用echo ${@} 替换myProgram 时发生了同样的事情;它打印的是文件名,而不是 glob。
    • 多啊。是的,${@} 会从 cmd 行 $1, $2 ... $n 获取参数,这意味着这些值已经扩展。所以之前的评论(我现在看不到),你需要发送用单引号括起来的参数,即myWrapper ... '*' ...祝你好运!
    【解决方案3】:

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

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

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

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

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

    这样的命令
    grep b[an]*a *.txt
    

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

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

    【讨论】:

    • 查看我的编辑。那么 ack 程序如何接受不带引号或反斜杠的 perl 正则表达式呢?
    • @Bryan:没有,请仔细查看输出(或查看我的编辑)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-12
    相关资源
    最近更新 更多