在子流程中使用*通配符的命令存在问题



我正在尝试使用子流程库和Popen方法将文件从一个位置复制到另一个位置。当运行以下脚本时,我得到了错误cp: cannot stat /some/dev_path/*。有人告诉我,*没有扩展到文件名,这就是问题所在。在其他一些帖子中,人们建议使用call而不是Popen,但据我所知,call不会返回stderr。

devPath = '/some/dev_path/'
productionPath = '/some/prod_path/'
p = subprocess.Popen(['cp', '-r', devPath + '*', productionPath], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
pout, perr = p.communicate()
if perr != '':
    sys.exit('Error: ' + perr)

扩展*(globbing)是shell的一个函数,例如bash。因此,您必须在subprocess.Popen调用中使用关键字参数shell=True

然而,对于这种情况,我强烈建议使用shutil.copytree。

(首先,因为它要简单得多(参见Python的Zen),而且不太容易出错。处理错误要干净得多,你会得到很好的异常,包括错误列表(对于像你这样的多文件操作),而且你不必处理生成子流程和与之通信的问题。其次,如果不需要,派生子流程是不必要的资源浪费。其他问题包括引用/转义,如果未能正确清理用户输入,可能会在代码中引入安全漏洞。)

例如:

from shutil import copytree
from shutil import Error
try:
   copytree('dir_a', 'dir_b')
except (Error, OSError), e:
    print "Attempt to copy failed: %s" % e

此外,您不应该通过将字符串连接在一起来构建文件系统路径,而是使用os.path.join()。这将为当前操作系统使用正确的目录分隔符(os.sep),并允许您轻松地编写可移植代码。

示例:

>>> import os
>>> os.path.join('/usr/lib', 'python2.7')
'/usr/lib/python2.7'

注意:os.path.join仍然只进行(智能)字符串操作——它不在乎该路径是否可访问或是否存在。

我在Python 3.x中遇到了完全相同的问题。我可以在Python 2.x中使用os.system()和command.command(),但现在需要使用Popen和运行子进程。

这个解决方案实际上非常简单,但在我看来并没有很好的记录。

使用shell=True时,传递一个字符串。

使用shell=False时,传递一个列表。

例如:

p = subprocess.Popen('cp -r ' + devPath + '* ' + productionPath, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell=True)
pout, perr = p.communicate()

我不明白为什么在我正在编写的一个使用大量复制命令的构建系统中,我会不断出现stat错误。这解决了所有的问题。

最新更新