【问题标题】:Subprocess command not finding files using ls command?子进程命令没有使用 ls 命令查找文件?
【发布时间】:2015-10-23 15:24:58
【问题描述】:

我正在创建一个程序,该程序将提取帐号列表,然后运行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: 无法访问 *CSV1000*APP*: 没有这样的文件或目录

但通过 Linux 相同的命令:

ls -lh *CSV*APP*

它按原样返回输出。

【问题讨论】:

    标签: python subprocess python-2.6


    【解决方案1】:

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

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

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

    【讨论】:

    • 我应该只使用 os 模块吗?似乎要简单得多,因为我可以像在 Linux 中那样将命令作为字符串输入。
    • 当然可以。这就是我会做的。你不会有像ls这样的任何外部依赖,它可以在其他操作系统上工作,而且没那么难......
    【解决方案2】:

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

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

    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)
    

    作为JuniorCompressor has pointed out,这仍然会在错误的目录中查找,因为cd 只会影响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 而不是underlying sp.Popen

    【讨论】:

    • 如果可以的话,我会的,不幸的是,我们在工作中运行的是 2.6:/
    • @flybonzai: 1. 传递cwd 参数而不是os.chdir() 的全局性较低(更好)。 2.很容易emulate check_output() on Python 2.6。 3. 您根本不需要调用ls:您可以使用glob 模块获取文件名和os.path.getsize() 获取文件大小或os.stat() 获取您可能需要的任何其他信息。
    【解决方案3】:

    您应该使用cwd argument to Popenshell=True,然后使用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 会更好;在您的情况下,您“仅”希望列出给定模式 (glob) 的文件并显示有关其大小 (stat) 的有序 (sorted) 信息。

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

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

    【讨论】:

    【解决方案4】:

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

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

    请参阅此处了解更多信息以及glob 的可能用法:Python subprocess wildcard usage

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多