启动包含管道命令的子进程时发现文件未找到错误

问题描述 投票:31回答:2

我需要在我的localhost上使用Python运行命令date | grep -o -w '"+tz+"'' | wc -w。我正在使用subprocess模块,并使用check_output方法,因为我需要捕获相同的输出。

但是它给我一个错误:

Traceback (most recent call last):
  File "test.py", line 47, in <module>
    check_timezone()
  File "test.py", line 40, in check_timezone
    count = subprocess.check_output(command)
  File "/usr/lib/python2.7/subprocess.py", line 537, in check_output
    process = Popen(stdout=PIPE, *popenargs, **kwargs)
  File "/usr/lib/python2.7/subprocess.py", line 679, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1249, in _execute_child
    raise child_exception-
OSError: [Errno 2] No such file or directory

请帮助我哪里出错了。我是python的新手

python shell subprocess pipe
2个回答
68
投票

您必须添加shell=True才能执行shell命令。 check_output正试图找到一个名为:date | grep -o -w '"+tz+"'' | wc -w的可执行文件,但他无法找到它。 (不知道为什么你从错误信息中删除了基本信息)。

看看之间的区别:

>>> subprocess.check_output('date | grep 1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.4/subprocess.py", line 603, in check_output
    with Popen(*popenargs, stdout=PIPE, **kwargs) as process:
  File "/usr/lib/python3.4/subprocess.py", line 848, in __init__
    restore_signals, start_new_session)
  File "/usr/lib/python3.4/subprocess.py", line 1446, in _execute_child
    raise child_exception_type(errno_num, err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'date | grep 1'

和:

>>> subprocess.check_output('date | grep 1', shell=True)
b'gio 19 giu 2014, 14.15.35, CEST\n'

阅读有关Frequently Used Arguments的文档,了解有关shell参数及其如何更改其他参数解释的更多信息。


请注意,您应该尽量避免使用shell=True,因为生成shell可能会带来安全隐患(即使您不执行不受信任的输入攻击,仍然可以执行Shellshock!)。

子进程模块的文档有一些关于replacing the shell pipeline的部分。您可以通过在python中生成两个进程并使用subprocess.PIPE来实现:

date_proc = subprocess.Popen(['date'], stdout=subprocess.PIPE)
grep_proc = subprocess.check_output(['grep', '1'], stdin=date_proc.stdout, stdout=subprocess.PIPE)
date_proc.stdout.close()
output = grep_proc.communicate()[0]

您可以编写一些简单的包装函数来轻松定义管道:

import subprocess
from shlex import split
from collections import namedtuple
from functools import reduce

proc_output = namedtuple('proc_output', 'stdout stderr')


def pipeline(starter_command, *commands):
    if not commands:
        try:
            starter_command, *commands = starter_command.split('|')
        except AttributeError:
            pass
    starter_command = _parse(starter_command)
    starter = subprocess.Popen(starter_command, stdout=subprocess.PIPE)
    last_proc = reduce(_create_pipe, map(_parse, commands), starter)
    return proc_output(*last_proc.communicate())

def _create_pipe(previous, command):
    proc = subprocess.Popen(command, stdin=previous.stdout, stdout=subprocess.PIPE)
    previous.stdout.close()
    return proc

def _parse(cmd):
    try:
        return split(cmd)
    except Exception:
        return cmd

有了这个,你可以写pipeline('date | grep 1')pipeline('date', 'grep 1')pipeline(['date'], ['grep', '1'])


5
投票

根据我的经验,FileNotFound与子进程的最常见原因是在命令中使用空格。请改用列表。

# Wrong, even with a valid command string
subprocess.run(["date | grep -o -w '\"+tz+\"' | wc -w"])

# Fixed
subprocess.run(["date", "|", "grep", "-o", "-w", "'\"+tz+\"'", "|", "wc", "-w"])

此更改不会导致FileNotFound错误,并且如果您使用更简单的命令搜索此异常,这是一个很好的解决方案。如果您使用的是python 3.5或更高版本,请尝试使用此方法:

import subprocess

a = subprocess.run(["date"], stdout=subprocess.PIPE)
print(a.stdout.decode('utf-8'))

b = subprocess.run(["grep", "-o", "-w", "'\"+tz+\"'"],
                   input=a.stdout, stdout=subprocess.PIPE)
print(b.stdout.decode('utf-8'))    

c = subprocess.run(["wc", "-w"],
                   input=b.stdout, stdout=subprocess.PIPE)
print(c.stdout.decode('utf-8'))

您应该看到一个命令的输出如何成为另一个输入,就像使用shell管道一样,但是您可以在python中轻松调试该过程的每个步骤。对于python> 3.5,建议使用subprocess.run,但在以前的版本中不可用。

© www.soinside.com 2019 - 2024. All rights reserved.