子进程中的bash globbing

时间:2014-03-13 21:47:33

标签: python bash

如果我有一个目录,我想删除除一个文件以外的所有文件,我可以在bash中执行此操作:

cd /tmp/a
rm -rf !(specialfile)
cd -

将此转换为最明显的Python代码对我来说失败了:

>>> subprocess.Popen( 'cd /tmp/a; rm -rf !(specialfile); cd -', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).communicate()

收到此消息:

('', "/bin/sh: -c: line 0: syntax error near unexpected token `('\n/bin/sh: -c: line 0: `cd /tmp/a; rm -rf !(specialfile); cd -'\n")

Python中的下一个最好的事情似乎是:

p = '/tmp/a'
    for i in os.listdir( p ):
    if i != 'specialfile':
        os.remove( os.path.join( p, i ) )

但当然这并不能很好地处理文件和子目录。还有更好的方法吗?

2 个答案:

答案 0 :(得分:2)

更新:正如@isedev和OP @JohnSchmitt在评论中指出的那样,subprocess.Popen会调用sh,而不是bash(以及sh可能或者可能不是bash,具体取决于平台),但使用扩展模式匹配运算符!(...)需要(a)bash和(b)extglob选项已转on(见下文背景)。

因此,答案是:

  • 使用通过bash命令行选项传递的命令字符串显式调用-c
  • 通过extglob命令行选项打开-O shell选项(没有它,glob !(specialfile)会触发OP遇到的语法错误。)

借用@ JohnSchmitt自己的评论,我们得到:

subprocess.Popen("bash -O extglob -c 'cd /tmp/a; rm -rf !(file2); cd -'",
  stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).communicate()

(不太优雅的替代方法是在shopt -s extglob;命令之前将rm添加到bash命令字符串。)

<强>背景

!(specialfile)扩展模式匹配运算符的实例(请参阅man bashPattern Matching部分);默认情况下,这些扩展运算符未启用; shopt -s extglob启用它们(shopt -u extglob禁用它们。)

答案 1 :(得分:1)

你可以使用os.walk作为@Bakuriu提到。非常重要的是从底部到顶部遍历目录树,以便始终具有空目录,但包含&#39; specialfile&#39;的目录除外。这就是为什么你需要try命令中的os.rmdir子句。

import os
for root, dirs, files in os.walk(top, topdown=False):
    for name in files:
        if name != 'specialfile':
            os.remove(os.path.join(root, name))
    for name in dirs:
        try:
            os.rmdir(os.path.join(root, name))
        except:
            pass