子进程命令未使用 ls 命令查找文件?

Subprocess command not finding files using ls command?

我正在创建一个程序,它将拉入一个帐号列表,然后 运行 一个 ls -lh 命令为每个帐号查找一个文件。当我在没有 Python 的 Linux 服务器上 运行 我的命令时,它没有问题地提取文件,但是当我通过 Python 执行它时,它说找不到他们。

import subprocess as sp
sp.call(['cd', input_dir])
for i, e in enumerate(piv_id_list):
    proc_out = sp.Popen(['ls', '-lh', '*CSV*APP*{0}.zip'.format(e)])
    proc_out_list.append(proc_out)
    print(proc_out)

这是我通过 Python 解释器 运行 命令时的一些示例输出:

>>> ls: cannot access *CSV1000*APP*: No such file or directory

但是通过Linux相同的命令:

ls -lh *CSV*APP*

它 returns 应该的输出。

这是因为 shell 会用与模式匹配的现有文件替换通配符。例如,如果您有 a.txtb.txt,那么 ls *.txt 将从 shell 扩展为 ls a.txt b.txt。使用您的命令,您实际上要求 ls 到 return 有关文件名中包含星号的文件的信息。如果要验证,请使用以下内容:

sp.Popen(['bash', '-c', 'ls', '-lh', '*CSV*APP*{0}.zip'.format(e)])

您还应该使用 os.chdir 更改目录,因为 sp.call(['cd', input_dir]) 更改了您创建的新进程的当前目录,而不是父进程。

我认为您需要将 shell=True 作为参数添加到 Popen 并用一个字符串替换列表:

proc_out = sp.Popen('ls -lh *CSV*APP*{0}.zip'.format(e), shell=True)

有关 glob 的更多信息和可能用法,请参阅此处:Python subprocess wildcard usage

您应该使用 cwd argument to Popen and shell=True, then communicate 来获取输出。

您的代码如下所示:

import subprocess as sp
for i, e in enumerate(piv_id_list):
    proc = sp.Popen(['ls', '-lh', '*CSV*APP*{0}.zip'.format(e)], cwd=input_dir, stdout=sp.PIPE, shell=True)
    proc_out_list.append(proc.communicate()[0])
    print(proc_out_list[-1])

但是为什么要创建子进程而不是使用标准库?

编辑

@tripleee所说,它只是替换了一些功能。 我认为尽可能使用 builtins/stdlib 更好;在您的情况下,您 "only" 想要列出给定模式 (glob) and show ordered (sorted) informations about their size (stat) 的文件。

使用stdlib 使您的代码更具可移植性;即使您不关心 Microsoft Windows 可移植性,您也可能希望避免 运行 使您的代码在没有 GNU binutils 的计算机上 运行 意外(即:Mac OS, BSD, ...).

您想使用 subprocess 模块来处理无法(轻松)在纯 Python 中实现的事情(即:使用 ffmpeg 编码视频,使用 ffmpeg 更改用户密码passwd,使用 sudo,...升级权限。

ls,从 运行 到 Python,可能是正确的:我猜 没有 文件叫 *CSV*APP*在当前目录中。可能有一个名称与该 glob 模式匹配的文件。但是 ls 不关心 glob。当您 运行 shell 上的命令时,会发生什么 shell 将 glob 扩展为它在当前目录中可以看到的匹配文件名,这些扩展名就是 shell 传递给 ls.

要在 shell 中获得与 Python 中相同的结果(为了演示,而不是因为你想要那样),请用单引号保护参数免受 glob 扩展:

ls -lh '*CVS*APP*'${e}'.zip'

但是如何在 Python 中获得 shell 的行为?您可以像其他一些答案所建议的那样使用 shell=True ,但这是一个滑坡,因为在动态生成的字符串上调用实际的 shell (可能取决于更复杂的应用程序中的用户输入)可以使您容易受到命令注入和其他恶意行为的影响。

在这里,您需要一个 shell、文件名通配的特定行为。而 Python 恰好能够做到这一点 all by itself:

import subprocess as sp
from glob import glob
sp.call(['cd', input_dir])
for i, e in enumerate(piv_id_list):
    proc_out = sp.Popen(['ls', '-lh', glob('*CSV*APP*{0}.zip'.format(e))])
    proc_out_list.append(proc_out)
    print(proc_out)

As ,这仍然会在错误的目录中查找,因为 cd 只会影响 of the cd调用,所以让我们也解决这个问题:

import subprocess as sp
from glob import glob

os.chdir(input_dir)
for i, e in enumerate(piv_id_list):
    proc_out = sp.Popen(['ls', '-lh', glob('*CSV*APP*{0}.zip'.format(e))])
    proc_out_list.append(proc_out)
    print(proc_out)
笔记

您可以直接使用 slightly higher-level sp.check_output instead of the underlying sp.Popen