【问题标题】:Sending piped commands via python3 subprocess通过 python3 子进程发送管道命令
【发布时间】:2016-03-19 09:24:33
【问题描述】:

我正在尝试通过 python3.4 执行以下子进程命令

cd /home/mailer-domains/domain | rndc loadkeys domain

我尝试了很多使用 .call 和 .Popen 的方法,但它要么不喜欢我的管道,要么不喜欢我的开关

>>> subprocess.call(['cd /home/mailer-domains/'+domain, '|', 'rndc', 'loadkeys', domain])    
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.4/subprocess.py", line 537, in call
    with Popen(*popenargs, **kwargs) as p:
  File "/usr/local/lib/python3.4/subprocess.py", line 859, in __init__
    restore_signals, start_new_session)
  File "/usr/local/lib/python3.4/subprocess.py", line 1457, in _execute_child
    raise child_exception_type(errno_num, err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'cd /home/mailer-domains/lecomm.com'

>>> subprocess.call(['cd /home/ex-mailer-domains/'+domain, '&&', 'rndc', 'loadkeys', domain]) 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.4/subprocess.py", line 537, in call
    with Popen(*popenargs, **kwargs) as p:
  File "/usr/local/lib/python3.4/subprocess.py", line 859, in __init__
    restore_signals, start_new_session)
  File "/usr/local/lib/python3.4/subprocess.py", line 1457, in _execute_child
    raise child_exception_type(errno_num, err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'cd /home/mailer-domains/lecomm.com'


>>> subprocess.call(['cd', '/home/mailer-domains/'+domain, '&&', 'rndc', 'loadkeys', domain])
cd: too many arguments
2


>>> subprocess.Popen(['cd', '/home/mailer-domains/'+domain, '&&', 'rndc', 'loadkeys', domain])    
<subprocess.Popen object at 0x805aa5860>
cd: too many arguments


>>> subprocess.call(['cd', '/home/mailer-domains/'+domain] '&&' ['rndc', 'loadkeys', domain])    
  File "<stdin>", line 1
    subprocess.call(['cd', '/home/mailer-domains/'+domain] '&&' ['rndc', 'loadkeys', domain])
                                                                 ^
SyntaxError: invalid syntax


>>> subprocess.call(['cd', '/home/mailer-domains/'+domain], '&&' ['rndc', 'loadkeys', domain])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: string indices must be integers


>>> subprocess.call(['cd', '/home/mailer-domains/'+domain], ['rndc', 'loadkeys', domain])     
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.4/subprocess.py", line 537, in call
    with Popen(*popenargs, **kwargs) as p:
  File "/usr/local/lib/python3.4/subprocess.py", line 767, in __init__
    raise TypeError("bufsize must be an integer")
TypeError: bufsize must be an integer


>>> subprocess.call(['cd', '/home/mailer-domains/'+domain] ['rndc', 'loadkeys', domain])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list indices must be integers, not tuple


>>> subprocess.call(['cd', '/home/mailer-domains/'+domain] +['rndc', 'loadkeys', domain])
cd: too many arguments
2


>>> ps = subprocess.Popen((['cd', '/home/mailer-domains/'+domain]), stdout = subprocess.PIPE)        
>>> output = subprocess.check_output((['rndc', 'loadkeys', domain]), stdin=ps.stdout)
rndc: 'loadkeys' failed: not found
no matching zone 'lecomm.com' in any view
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.4/subprocess.py", line 620, in check_output
    raise CalledProcessError(retcode, process.args, output=output)
subprocess.CalledProcessError: Command '['rndc', 'loadkeys', 'lecomm.com']' returned non-zero exit status 1


>>> output = subprocess.check_output((['rndc', 'loadkeys', domain]), stdin=ps.stdout, stdout=PIPE)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'PIPE' is not defined

如何通过 Python3 subprocess 正确发送 2 个管道命令?

【问题讨论】:

  • 您想通过将cd(通常为空)的输出通过管道传输到rndc 进程来实现什么目的?你真的只想在新目录中运行rndc
  • @TomDalton 我假设 loadkey 开关在当前目录中查找。我看到一些奇怪的行为表明了这一点。我有许多区域目录,每个目录都有自己的键。
  • 不过,我在想,这听起来很傻。

标签: python python-3.x pipe subprocess


【解决方案1】:

您根本不需要管道,只需将cwd 传递给子进程:

subprocess.call(['rndc', 'loadkeys', domain],cwd='/home/mailer-domains/'+domain)

如果您确实想要更改的不仅仅是子进程的目录,您应该使用os.chdir

【讨论】:

    【解决方案2】:

    答案可以在subprocess documentation找到。

    subprocess 模块中的函数通常不调用 shell 来解释命令,而是使用给定的参数直接调用它们!可以使用参数shell=True(Python 文档中的示例)覆盖此行为:

    output = check_output("dmesg | grep hda", shell=True)
    

    但是,如果命令和参数不固定但取决于用户输入,则不建议这样做。然后,执行此操作的正确方法是使用两个 Popen 调用并手动构建管道(代码示例再次来自 Python 文档):

    p1 = Popen(["dmesg"], stdout=PIPE)
    p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
    p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
    output = p2.communicate()[0]
    

    【讨论】:

    • 根本不需要管道。 cwd 可以传递给子进程
    • @PadraicCunningham 在这种情况下同意。但是,搜索此问题标题的人几乎肯定会对此答案感兴趣。
    • 有1000个重复的管道,这不是OP采取的方法,当你可以通过目录时管道没有意义
    • @PadraicCunningham: 1001 ... :D
    • 它确实帮助了我!谢谢@Sebastian Riese