Python的子进程模块从Unix shell返回不同的结果

时间:2010-07-01 22:29:58

标签: python unix

我正在尝试使用python获取目录中的CSV文件列表。这在unix中非常简单:

ls -l *.csv

而且,可以预见的是,我得到了一个以我目录中的.csv结尾的文件列表。但是,当我使用Subprocess模块​​尝试Python等价物时:

>>> import subprocess as sp
>>> sp.Popen(["ls", "-l", "*.csv"], stdout = sp.PIPE)
<subprocess.Popen object at 0xb780e90c>
>>> ls: cannot access *.csv: No such file or directory

有人可以解释一下发生了什么吗?

修改:添加shell = True会删除错误,但我没有获取仅包含CSV文件的列表,而是获取所有文件列表目录。

4 个答案:

答案 0 :(得分:4)

如果你希望它的行为与shell一样,你需要通过shell=True(你的里程可能会有所不同,具体取决于你的系统和shell)。在您的情况下,问题是当您执行ls -l *.csv时, shell 正在评估*表示什么,而不是ls。 (ls仅仅是格式化您的结果,但是shell已经完成了繁重的任务以确定哪些文件匹配*.csv)。子流程使ls按字面意思处理*.csv,并查找具有该特定名称的文件,当然没有任何文件(因为这是一个非常难以创建的文件名)。

你真正应该做的是使用os.listdir并自己过滤名称。

答案 1 :(得分:4)

为什么不使用glob呢?它会比“炮轰”更快!

import glob
glob.glob('*.csv')

这为您提供了名称,而不是所有额外信息ls -l耗材,但您可以通过os.stat调用感兴趣的文件来获取额外信息。

如果你真的必须使用ls -l,我想你想把它作为一个字符串传递给shell进行所需的星形扩展:

proc = sp.Popen('ls -l *.csv', shell=True, stdout=sp.PIPE)

答案 2 :(得分:1)

当您在shell中输入ls -l *.csv时,shell本身会将*.csv扩展为其匹配的所有文件名列表。所以ls的参数实际上更像是ls -l spam.txt eggs.txt ham.py

ls命令本身不了解通配符。因此,当您将参数*.csv传递给它时,它会尝试将其视为文件名,并且没有具有该名称的文件。正如Nick所说,您可以使用shell=True参数让Python调用shell来运行子进程,shell将为您扩展通配符。

答案 3 :(得分:1)

p=subprocess.Popen(["ls", "-l", "*.out"], stdout = subprocess.PIPE, shell=True)

原因

/bin/sh -c ls -l *.out

要执行。

如果在目录中尝试此命令,您将看到 - 以典型的mystifying-shell方式 - 列出所有文件。并且-l标志也被忽略。这是一个线索。

您看,-c标记仅提取ls。其余的论点被/bin/sh吃掉了,而不是被ls吃掉了。

要使此命令在终端上正常工作,您必须键入

/bin/sh -c "ls -l *.out"

现在/ bin / sh将完整命令“ls -l * .out”视为-c标志的参数。

因此,要使用subprocess.Popen来解决这个问题,最好将命令作为单个字符串传递

p=subprocess.Popen("ls -l *.out", stdout = subprocess.PIPE, shell=True)
output,error=p.communicate()
print(output)