使用subprocess运行多个bash命令

88

如果我在bash中运行echo a; echo b,结果是两个命令都被执行。但是如果我使用subprocess,则只会运行第一个命令,并打印出整个剩余部分的内容。 下面的代码输出a; echo b而不是a b,我该如何运行这两个命令?

import subprocess, shlex
def subprocess_cmd(command):
    process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
    proc_stdout = process.communicate()[0].strip() 
    print proc_stdout

subprocess_cmd("echo a; echo b")

1
相关:这里是如何同时运行多个shell命令(并可选择捕获它们的输出)的方法。 - jfs
7个回答

123

您必须在subprocess中使用shell=True,而不是shlex.split:

import subprocess

command = "echo a; echo b"

ret = subprocess.run(command, capture_output=True, shell=True)

# before Python 3.7:
# ret = subprocess.run(command, stdout=subprocess.PIPE, shell=True)

print(ret.stdout.decode())

返回:

a
b

8
我明白了,我曾测试过 shell=True ,但是使用 command.split() 将它分割成列表时出现问题。还需要注意的是,对于其他人阅读此信息,使用 shell=True 可能存在安全隐患,请确保你信任输入内容。 - Paul
3
你不能在 shell=True 的情况下使用 command.split()。实际上,subprocess.Popenshell=True 的参数必须是一个字符串而不是一个列表。 - bougui
1
我发现这个代码更简单,而且输出没有乱码: def do_shell(self, command): self.proc=subprocess.Popen(command,shell=True) self.proc.wait() - Goblinhack
2
@Ravichandra:注意:标题中的“bash”。在Windows上,您可能需要使用echo a&echo b命令。 - jfs
在Windows上这个不起作用,它会返回"a; echo b"。 - Milan
显示剩余2条评论

32

我刚刚遇到了一个情况,需要在Python中运行一堆没有用分号分隔的Bash代码。在这种情况下,提出的解决方案都无法帮助我。一种方法是保存一个文件,然后使用Popen运行它,但在我的情况下不可能。

最终我采取的做法类似于:

commands = '''
echo "a"
echo "b"
echo "c"
echo "d"
'''

process = subprocess.Popen('/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE)
out, err = process.communicate(commands)
print out

我首先创建子bash进程,之后告诉它要执行什么。这种方法消除了直接将命令传递给Popen构造函数的限制。


5
subprocess.check_output(commands, shell=True) 可以正常工作。如果commands参数中包含bash语法,则需添加executable='/bin/bash' - jfs
11
对于 Python 3,您可能需要使用以下代码:out, err = process.communicate(commands.encode('utf-8'))print(out.decode('utf-8')) - Eyal Levin
如果命令是以 ; 分隔的,您可能还想在 subprocess.Popen() 行中添加 shell=True - ssanch

20

使用"&&"连接命令。

os.system('echo a > outputa.txt && echo b > outputb.txt')

5
如果第一个命令出现错误,第二个命令将不会运行。我建议使用分号 ; 代替。 - NateW
4
这里的重要部分是使用 os.system() 而不是 subprocess - Murphy
1
os.system不是subprocess的替代品。 os.system无法完成subprocess所能做的所有事情。 - Dan Niero
也许不行,但是它可以很好地“回显”。 - Captain Jack Sparrow
subprocess比os.system更受欢迎,但没有人给出适用于subprocess和shell=False(或类似)的答案。 - Jonathan Rayner

4

如果您只需一次运行命令,那么可以使用subprocess.check_output方便函数:

def subprocess_cmd(command):
    output = subprocess.check_output(command, shell=True)
    print output

1

当我使用capture_output=True时,出现了类似于以下的错误

TypeError: __init__() got an unexpected keyword argument 'capture_output'

在进行以下更改后,它可以正常工作:

import subprocess

command = '''ls'''

result = subprocess.run(command, stdout=subprocess.PIPE,shell=True)

print(result.stdout.splitlines())

“capture_output” 是在 Python 3.7 中才引入的,而现在已经相当古老了,但显然你的版本比它还要旧。 - tripleee
这里shell=True是无用的;ls可以在没有shell的情况下正常运行,尽管名义上应该将命令作为列表["ls"]传递。 - tripleee

1
>>> command = "echo a; echo b"
>>> shlex.split(command);
    ['echo', 'a; echo', 'b']

所以,问题在于 shlex 模块不能处理 ";"。

command.split() 返回 ['echo', 'a;', 'echo', 'b'] 并且失败了。 - Paul
1
@bougui 是正确的。设置 "shell=True",Popen 的第一个参数需要像 "echo a;echo b" 这样的命令字符串。而没有 "shell=True",Popen 的第一个参数应该是一个列表,如:["echo","a"]。 - David.Zheng

0
import subprocess
cmd = "vsish -e ls /vmkModules/lsom/disks/  | cut -d '/' -f 1  | while read diskID  ; do echo $diskID; vsish -e cat /vmkModules/lsom/disks/$diskID/virstoStats | grep -iE 'Delete pending |trims currently queued' ;  echo '====================' ;done ;"


def subprocess_cmd(command):
    process = subprocess.Popen(command,stdout=subprocess.PIPE, shell=True)
    proc_stdout = process.communicate()[0].strip()
    for line in proc_stdout.decode().split('\n'):
        print (line)

subprocess_cmd(cmd)

欢迎来到StackOverflow!请解释一下你提供的代码如何回答原始问题。 - effprime
运行命令时使用管道符(|)。 - neelam gour

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接