我需要在我的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的新手
答案 0 :(得分:64)
您必须添加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'])
答案 1 :(得分:3)
根据我的经验,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&gt;,建议使用subprocess.run; 3.5,但在以前的版本中不可用。
答案 2 :(得分:2)
发生FileNotFoundError的原因是-在没有shell=True
的情况下-Python试图查找其文件名就是您要传入的整个字符串的可执行文件。您需要添加shell=True
才能将Shell移至解析并执行字符串,或者弄清楚如何重新构建此命令行以避免使用shell。
这里的shell编程绝对很奇怪。在任何普通系统上,date
绝对不会输出"+tz+"
,因此其余的处理是没有意义的。
此外,使用wc -w
来计数来自grep
的输出单词数是不寻常的。更为常见的用例(如果不能简单地使用grep -c
来计算匹配行的数量)将是使用wc -l
来计算grep
的输出行。>
如果可以,you want to avoid shell=True
;如果此处的目的是测试date
命令,则可能应该用本机Python代码替换其余的Shell脚本。
优点:
date
),而不需要类似Unix的平台。缺点:
为了避免这种情况,如果只想计算"+tz+"
的输出中date
出现多长时间,请尝试
p = subprocess.run(['date'],
capture_output=True, text=True,
check=True)
result = len(p.stdout.split('"+tz+"'))-1
关键字参数text=True
需要Python 3.7;为了与早期Python版本兼容,请尝试使用(误称)旧式同义词universal_newlines=True
。对于非常老的Python版本,可能会退回到subprocess.check_output()
。
如果您确实需要-w
的{{1}}选项的语义,则需要检查与该匹配项相邻的字符是否不是字母,并排除那些。我将其保留为练习,实际上是假设此处的原始shell脚本实现实际上是不正确的。 (也许尝试grep
。)
在更普通的情况下(单个命令,没有管道,通配符,重定向,shell内置等),您可以使用Python的re.split(r'(?<=^|\W)"\+tz\+"(?=\W|$)', p.stdout)
将命令解析为正确引用的参数列表。例如,
shlex.split()
请注意,常规字符串>>> import shlex
>>> shlex.split(r'''one "two three" four\ five 'six seven' eight"'"nine'"'ten''')
['one', 'two three', 'four five', 'six seven', 'eight\'nine"ten']
在这里完全不合适;它只是在每个空格字符上分割,不支持任何形式的引号或转义。
答案 3 :(得分:0)
该问题已经在上面给出了答案,但以防万一这些解决方案不适合您;请检查路径本身以及是否为进程设置了所有环境变量以定位路径。
答案 4 :(得分:0)
在 python 3.8.10 上对我有用的东西(受到这里的@mightypile 解决方案的启发:https://stackoverflow.com/a/49986004/12361522),删除了参数拆分,我也必须启用 shell:>
这个:
c = subprocess.run(["wc -w"], input=b.stdout, stdout=subprocess.PIPE, shell=True)
代替:
c = subprocess.run(["wc", "-w"], input=b.stdout, stdout=subprocess.PIPE)
如果有人想尝试我的解决方案(至少对于 v3.8.10),这是我的:
我有包含至少 2 种文件类型(.jpg 和其他)的多个文件的目录。我需要通过 1 个管道计算特定文件类型 (.jpg) 而不是目录中的所有文件:
ls *.jpg | wc -l
所以最终我让它像这里一样工作:
import subprocess
proc1 = subprocess.run(["ls *.jpg"], stdout=subprocess.PIPE, shell=True)
proc2 = subprocess.run(['wc -l'], input=proc1.stdout, stdout=subprocess.PIPE, shell=True)
print(proc2.stdout.decode())
它不适用于拆分:
["ls", "*.jpg"]
会使 ls
忽略约束 *.jpg
['wc', '-l']
将返回正确的计数,但会返回所有 3 个输出,而不仅仅是我所追求的一个
如果没有启用 shell shell=True