【问题标题】:Subprocess command shows FileNotFoundError: [Errno 2] No such file or directory子进程命令显示 FileNotFoundError: [Errno 2] No such file or directory
【发布时间】:2022-01-06 15:25:03
【问题描述】:

我正在尝试通过在下面的代码中使用 subprocess 模块来使用 python 运行 shell 命令,但我不知道为什么我的脚本会抛出如下所示的错误。有人可以帮我解决我所缺少的吗?

Traceback (most recent call last):
  File "/Scripts/test_file.py", line 6, in <module>
    p2 = subprocess.Popen('sed s\'/&quot;/ /g\'', stdin=p1.stdout, stdout=subprocess.PIPE)
  File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/subprocess.py", line 854, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/subprocess.py", line 1702, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: "sed s'/&quot;/ /g'"`

import subprocess
#output3.txt='/Users/boggulv/Desktop/output3.txt'
p1 = subprocess.Popen( ['cat', 'output3.txt'], stdout=subprocess.PIPE)
print(p1)
p2 = subprocess.Popen('sed s\'/&quot;/ /g\'', stdin=p1.stdout, stdout=subprocess.PIPE)
p3 = subprocess.Popen('grep "sO"', stdin=p2.stdout,stdout=subprocess.PIPE)
p4 = subprocess.Popen('grep -v "get"', stdin=p3.stdout, stdout=subprocess.PIPE)
p5 = subprocess.Popen('cut -d \',\' -f2', stdin=p4.stdout, stdout=subprocess.PIPE)
p6 = subprocess.Popen('sed \'s/"//g\'', stdin=p5.stdout, stdout=subprocess.PIPE)
p7 = subprocess.Popen('sort', stdin=p6.stdout, stdout=subprocess.PIPE)
p8 = subprocess.Popen('sort', stdin=p8.stdout, stdout=subprocess.PIPE)
p9 = subprocess.Popen('uniq -c', stdin=p8.stdout, stdout=subprocess.PIPE)
p0 = subprocess.Popen('sort -nr', stdin=p9.stdout, stdout=subprocess.PIPE)
print(p01.communicate())

现在尝试更改为列表。

p2 = subprocess.Popen('sed \'s/&quot;/ /g\'', stdin=p1.stdout, stdout=subprocess.PIPE, shell=True)
p3 = subprocess.Popen(['grep','"shipOption"'], stdin=p2.stdout,stdout=subprocess.PIPE,shell = True)
p4 = subprocess.Popen(['grep','-v', '"getShipMethod"'], stdin=p3.stdout, stdout=subprocess.PIPE,shell = True)
p5 = subprocess.Popen(['cut','-d','\',\'', '-f2'], stdin=p4.stdout, stdout=subprocess.PIPE,shell = True)
p6 = subprocess.Popen(['sed','\'s/"//g\''],stdin=p5.stdout, stdout=subprocess.PIPE,shell = True)
p7 = subprocess.Popen(['sort'], stdin=p6.stdout, stdout=subprocess.PIPE,shell = True)
p8 = subprocess.Popen(['uniq', '-c'], stdin=p7.stdout, stdout=subprocess.PIPE,shell = True)
p9 = subprocess.Popen(['sort', '-nr'], stdin=p8.stdout, stdout=subprocess.PIPE,shell = True)
p0 = subprocess.Popen(['head', '-10'], stdin=p9.stdout, stdout=subprocess.PIPE,shell = True)```

New Error:

`usage: grep [-abcdDEFGHhIiJLlMmnOopqRSsUVvwXxZz] [-A num] [-B num] [-C[num]]
        [-e pattern] [-f file] [--binary-files=value] [--color=when]
        [--context[=num]] [--directories=action] [--label] [--line-buffered]
        [--null] [pattern] [file ...]
usage: grep [-abcdDEFGHhIiJLlMmnOopqRSsUVvwXxZz] [-A num] [-B num] [-C[num]]
        [-e pattern] [-f file] [--binary-files=value] [--color=when]
        [--context[=num]] [--directories=action] [--label] [--line-buffered]
        [--null] [pattern] [file ...]
usage: cut -b list [-n] [file ...]
       cut -c list [file ...]
       cut -f list [-s] [-w | -d delim] [file ...]
(b'', None)
cat: stdin: Input/output error`

【问题讨论】:

  • 对于传递给sed 的参数,您可能需要使用双引号字符 (") — 我认为 bash 无法按照您的方式识别单引号 (') 字符想想。
  • 您要么需要传递[commandname, argument, ...] 列表,要么需要传递'commandname arguments ... 字符串 shell=True。除了第一次调用之外,您使用的是字符串形式但没有shell=True,因此整个字符串被视为命令名称,而不是解析为命令名称和参数。这看起来像是 "Actual meaning of shell=True in subprocess" 的副本。
  • 您为什么要使用subprocess 而不是仅仅编写代码(或者可能编写一个shell 脚本)来做这些事情? Python 可以轻松完成所有这些操作,而无需增加十几个外部 shell 进程的开销。
  • 以上部分只是一个功能,我在其他功能中使用了其他shell命令,这一步我有一个阻塞器

标签: python shell subprocess


【解决方案1】:

您的命令仍然是错误的。如果你只是想像 shell 一样运行这些命令,最简单的方法就是……使用 shell。

result = subprocess.run('''
# useless cat, but bear with
cat output3.txt |
sed 's/&quot;/ /g' |
grep "shipOption" |
grep -v "getShipMethod" |
cut -d ',' -f2 |
sed 's/"//g' |
sort |
uniq -c |
sort -nr |
head -10
    ''',
    # Probably add these too
    check=True,
    capture_output=True,
    # We are using the shell for piping etc
    shell=True)

如果您想删除shell=True 并手动运行所有这些进程,您必须了解shell 的工作原理。特别是,您需要修复引用,以便您运行的命令具有保留在 shell 处理了句法引号之后的引号。

p1 = subprocess.Popen(['cat', 'output3.txt'], stdout=subprocess.PIPE)  # still useless
p2 = subprocess.Popen(['sed','s/&quot;/ /g'], stdin=p1.stdout, stdout=subprocess.PIPE)
p3 = subprocess.Popen(['grep', "shipOption"], stdin=p2.stdout,stdout=subprocess.PIPE)
p4 = subprocess.Popen(['grep', '-v', "getShipMethod"], stdin=p3.stdout, stdout=subprocess.PIPE)
p5 = subprocess.Popen(['cut', '-d', ',', '-f2'], stdin=p4.stdout, stdout=subprocess.PIPE)
p6 = subprocess.Popen(['sed', 's/"//g'],stdin=p5.stdout, stdout=subprocess.PIPE)
p7 = subprocess.Popen(['sort'], stdin=p6.stdout, stdout=subprocess.PIPE)
p8 = subprocess.Popen(['uniq', '-c'], stdin=p7.stdout, stdout=subprocess.PIPE)
p9 = subprocess.Popen(['sort', '-nr'], stdin=p8.stdout, stdout=subprocess.PIPE)
p0 = subprocess.Popen(['head', '-10'], stdin=p9.stdout, stdout=subprocess.PIPE)

请特别注意 sedgrep 的参数是如何删除其外部引号的,以及我们如何在任何地方删除 shell=True。根据经验,如果Popen(或其他subprocess 方法)的第一个参数是一个列表,则不应使用shell=True,反之亦然。 (在某些情况下,您可以将列表传递给shell=True,但是......我们甚至不要开始去那里。)

不过,所有这些似乎都没有实际意义,因为 Python 可以出色地完成所有这些事情。

from collections import Counter

counts = Counter()
with open('output3.txt', 'r', encoding='utf-8') as lines:
    for line in lines:
        line = line.rstrip('\n').replace('&quot;', ' ')
        if "shipOption" in line and "getShipMethod" not in line:
            field = line.split(',')[1].replace('"', '')
            counts[field] += 1
print(counts.most_common(10))

您可能希望将rstripreplace 放在if 中以避免不必要的工作。当然,可以对 shell 管道进行相同的重构。

【讨论】:

【解决方案2】:

按照上面的 Gordon - 默认情况下 Popen() 对待

sed 's/&quot;/ /g'

作为要运行的命令的名称,而不是命令名称加一个参数。因此,您需要执行以下操作之一:

p2 = subprocess.Popen(['sed', 's/&quot;/ /g'], stdin=p1.stdout, stdout=subprocess.PIPE)

p2 = subprocess.Popen('sed \'s/&quot;/ /g\'', stdin=p1.stdout, stdout=subprocess.PIPE, shell=True)

使用 shell=True 可以让 Popen 函数将字符串拆分为一个包含命令和参数的列表。

另请注意,p0 是您的最后一个结果,但您调用 p01.communicate()

【讨论】:

    猜你喜欢
    • 2022-01-24
    • 1970-01-01
    • 2018-03-07
    • 1970-01-01
    • 2012-08-25
    • 2019-10-14
    • 1970-01-01
    • 2023-03-06
    相关资源
    最近更新 更多