如何执行程序或调用系统命令?
Posted
技术标签:
【中文标题】如何执行程序或调用系统命令?【英文标题】:How to execute a program or call a system command? 【发布时间】:2010-09-10 11:51:11 【问题描述】:如何从 Python 脚本中调用外部命令(就像我在 Unix shell 或 Windows 命令提示符下输入的一样)?
【问题讨论】:
我不明白,import os; os.system('pip list | grep anatome')
有什么问题?至少这允许您像我的示例所示那样进行管道处理。目前尚不清楚如何使用import subprocess; subprocess.run(["ls", "-l"])
。
【参考方案1】:
使用标准库中的subprocess
模块:
import subprocess
subprocess.run(["ls", "-l"])
subprocess.run
相对于os.system
的优势在于它更灵活(您可以获得stdout
、stderr
、"real" status code、更好的error handling 等...)。
即使the documentation for os.system
也建议使用subprocess
代替:
subprocess
模块为生成新进程和检索其结果提供了更强大的工具;使用该模块优于使用此功能。请参阅subprocess
文档中的 Replacing Older Functions with the subprocess Module 部分,了解一些有用的食谱。
在 Python 3.4 及更早版本上,使用 subprocess.call
而不是 .run
:
subprocess.call(["ls", "-l"])
【讨论】:
有没有办法使用变量替换? IE 我尝试使用call(["echo", "$PATH"])
来执行echo $PATH
,但它只是回显了文字字符串$PATH
,而不是进行任何替换。我知道我可以获得 PATH 环境变量,但我想知道是否有一种简单的方法可以让命令的行为与我在 bash 中执行它时完全一样。
@KevinWheeler 你必须使用shell=True
才能工作。
@KevinWheeler 你不应该使用shell=True
,为此Python自带了os.path.expandvars。在您的情况下,您可以写:os.path.expandvars("$PATH")
。 @SethMMorton 请重新考虑您的评论-> Why not to use shell=True
如果我想通过管道传输东西怎么办? pip list | grep anatome
?
我不明白,import os; os.system('pip list | grep anatome')
有什么问题?至少这允许您像我的示例所示那样进行管道处理。目前尚不清楚如何使用import subprocess; subprocess.run(["ls", "-l"])
来做到这一点。【参考方案2】:
调用外部程序的方法总结,包括它们的优缺点:
os.system
将命令和参数传递给系统的 shell。这很好,因为您实际上可以以这种方式一次运行多个命令并设置管道和输入/输出重定向。例如:
os.system("some_command < input_file | another_command > output_file")
然而,虽然这很方便,但您必须手动处理 shell 字符(例如空格等)的转义。另一方面,这也允许您运行只是 shell 命令而不是实际外部程序的命令。
os.popen
将做与os.system
相同的事情,只是它为您提供了一个类似文件的对象,您可以使用该对象来访问该进程的标准输入/输出。 popen 还有 3 个其他变体,它们对 i/o 的处理都略有不同。如果您将所有内容都作为字符串传递,那么您的命令将传递给 shell;如果您将它们作为列表传递,那么您无需担心转义任何内容。示例:
print(os.popen("ls -l").read())
subprocess.Popen
。这旨在替代os.popen
,但缺点是由于过于全面而稍微复杂一些。例如,你会说:
print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()
而不是
print os.popen("echo Hello World").read()
但是很高兴将所有选项放在一个统一的类中,而不是 4 个不同的 popen 函数。见the documentation。
subprocess.call
。这基本上就像Popen
类并接受所有相同的参数,但它只是等待命令完成并为您提供返回码。例如:
return_code = subprocess.call("echo Hello World", shell=True)
subprocess.run
。仅限 Python 3.5+。与上述类似,但更加灵活,在命令执行完成时返回CompletedProcess
对象。
os.fork
、os.exec
、os.spawn
与它们的 C 语言对应物相似,但我不建议直接使用它们。
subprocess
模块应该是您使用的。
最后,请注意,对于将最终命令作为字符串传递给 shell 执行的所有方法,您有责任将其转义。 如果您传递的字符串的任何部分不能被完全信任,则会有严重的安全隐患。例如,如果用户正在输入字符串的某些/任何部分。如果您不确定,请仅将这些方法与常量一起使用。为了给您暗示暗示,请考虑以下代码:
print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()
并想象用户输入了“my mama didnt love me && rm -rf /
”,这可能会擦除整个文件系统。
【讨论】:
很好的答案/解释。这个答案如何证明本文所述的 Python 座右铭? fastcompany.com/3026446/… “从风格上讲,Perl 和 Python 有不同的哲学。Perl 最著名的座右铭是“有不止一种方法可以做到”。Python 被设计为有一种明显的方法来做到这一点“看起来应该是另一种方法!在 Perl 中,我只知道执行命令的两种方法 - 使用反引号或open
。
如果使用 Python 3.5+,请使用 subprocess.run()
。 docs.python.org/3.5/library/subprocess.html#subprocess.run
人们通常需要知道的是子进程的 STDOUT 和 STDERR 做了什么,因为如果它们被忽略,在某些(相当常见的)条件下,最终子进程将发出系统调用写入STDOUT(也有STDERR?),这将超过操作系统为进程提供的输出缓冲区,并且操作系统将导致它阻塞,直到某个进程从该缓冲区读取。那么,按照目前推荐的方式,subprocess.run(..)
,"This does not capture stdout or stderr by default." 究竟意味着什么? subprocess.check_output(..)
和 STDERR 呢?
您推荐的哪些命令会阻止我的脚本?即,如果我想在 for
循环中运行多个命令,我该怎么做而不阻塞我的 python 脚本?我不关心命令的输出,我只想运行很多命令。
这可以说是错误的方式。大多数人只需要subprocess.run()
或其兄弟姐妹subprocess.check_call()
等。对于这些还不够的情况,请参阅subprocess.Popen()
。 os.popen()
可能根本不应该被提及,或者甚至在“破解你自己的 fork/exec/spawn 代码”之后出现。【参考方案3】:
典型实现:
import subprocess
p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
print line,
retval = p.wait()
您可以随意使用管道中的stdout
数据做您想做的事。实际上,您可以简单地省略这些参数(stdout=
和 stderr=
),它的行为就像 os.system()
。
【讨论】:
.readlines()
一次读取 所有 行,即它会阻塞直到子进程退出(关闭其管道末端)。要实时阅读(如果没有缓冲问题),您可以:for line in iter(p.stdout.readline, ''): print line,
您能否详细说明“如果没有缓冲问题”是什么意思?如果进程确实阻塞,则子进程调用也会阻塞。我原来的例子也可能发生同样的情况。在缓冲方面还会发生什么?
子进程可能在非交互模式下使用块缓冲而不是行缓冲,所以p.stdout.readline()
(注意:最后没有s
)在子进程之前不会看到任何数据填充其缓冲区。如果孩子没有产生太多数据,那么输出将不是实时的。请参阅Q: Why not just use a pipe (popen())? 中的第二个原因。提供了一些解决方法in this answer (pexpect, pty, stdbuf)
缓冲问题仅在您希望实时输出时才重要,并且不适用于在收到 所有 数据之前不打印任何内容的代码
这个答案在当时还不错,但我们不应该再推荐 Popen
用于简单的任务。这也不必要地指定了shell=True
。尝试subprocess.run()
答案之一。【参考方案4】:
关于将子进程与调用进程分离的一些提示(在后台启动子进程)。
假设你想从一个 CGI 脚本开始一个长任务。也就是说,子进程的寿命应该比 CGI 脚本执行进程长。
子流程模块文档中的经典示例是:
import subprocess
import sys
# Some code here
pid = subprocess.Popen([sys.executable, "longtask.py"]) # Call subprocess
# Some more code here
这里的想法是,您不想在“调用子进程”行中等待,直到 longtask.py 完成。但尚不清楚示例中的“这里有更多代码”行之后会发生什么。
我的目标平台是 FreeBSD,但开发是在 Windows 上,所以我先在 Windows 上遇到问题。
在 Windows (Windows XP) 上,父进程在 longtask.py 完成其工作之前不会完成。这不是您在 CGI 脚本中想要的。该问题并非特定于 Python;在 php 社区,问题是一样的。
解决方案是将 DETACHED_PROCESS Process Creation Flag 传递给 Windows API 中的底层 CreateProcess 函数。 如果你碰巧安装了pywin32,你可以从win32process模块导入flag,否则你应该自己定义:
DETACHED_PROCESS = 0x00000008
pid = subprocess.Popen([sys.executable, "longtask.py"],
creationflags=DETACHED_PROCESS).pid
/* UPD 2015.10.27 @eryksun 在下面的评论中指出,语义正确的标志是 CREATE_NEW_CONSOLE (0x00000010) */
在 FreeBSD 上我们还有另一个问题:当父进程结束时,它也结束了子进程。这也不是您想要的 CGI 脚本。一些实验表明问题似乎在于共享 sys.stdout。工作解决方案如下:
pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
我没有检查过其他平台上的代码,不知道在 FreeBSD 上出现这种行为的原因。如果有人知道,请分享您的想法。谷歌搜索在 Python 中启动后台进程还没有发现任何问题。
【讨论】:
您可能还需要 CREATE_NEW_PROCESS_GROUP 标志。见Popen waiting for child process even when the immediate child has terminated 我看到import subprocess as sp;sp.Popen('calc')
没有等待子流程完成。似乎没有必要创建标志。我错过了什么?
@ubershmekel,我不确定你的意思,也没有安装 Windows。如果我没记错的话,如果没有标志,您将无法关闭您启动 calc
的 cmd
实例。
以下是不正确的:“[o]n windows (win xp),直到 longtask.py 完成它的工作,父进程才会完成”。父进程会正常退出,但控制台窗口(conhost.exe 实例)仅在最后一个附加进程退出时才会关闭,并且子进程可能继承了父进程的控制台。在creationflags
中设置DETACHED_PROCESS
通过阻止子继承或创建控制台来避免这种情况。如果您想要一个新的控制台,请使用CREATE_NEW_CONSOLE
(0x00000010)。
我并不是说作为分离进程执行是不正确的。也就是说,您可能需要为文件、管道或os.devnull
设置标准句柄,因为否则某些控制台程序会出现错误退出。当您希望子进程与父进程同时与用户交互时,请创建一个新控制台。尝试在一个窗口中同时执行这两项操作会令人困惑。【参考方案5】:
import os
os.system("your command")
请注意,这是危险的,因为该命令没有被清除。我把它留给你去谷歌搜索'os'和'sys'模块的相关文档。有很多函数(exec* 和 spawn*)可以做类似的事情。
【讨论】:
不知道我在近十年前的意思(检查日期!),但如果我不得不猜测,那就是没有完成验证。 这应该指向subprocess
,它是一种更通用和更便携的解决方案。运行外部命令当然本质上是不可移植的(您必须确保该命令在您需要支持的每个架构上都可用)并且将用户输入作为外部命令传递本质上是不安全的。
请注意这个人的时间戳:“正确”答案的票数是 40 倍,是答案 #1。
对我来说运行 NodeJS 东西的一种解决方案。【参考方案6】:
我建议使用 subprocess 模块而不是 os.system,因为它会为您进行 shell 转义,因此更安全。
subprocess.call(['ping', 'localhost'])
【讨论】:
如果你想从一个带参数的命令中创建一个列表,一个可以在shell=False
时与subprocess
一起使用的列表,然后使用shlex.split
一个简单的方法来做到这一点docs.python.org/2/library/shlex.html#shlex.split(根据文档docs.python.org/2/library/subprocess.html#popen-constructor,这是推荐的方法)
这是不正确的:“它会为你进行 shell 转义,因此更安全”。 subprocess 不进行 shell 转义, subprocess 不会通过 shell 传递您的命令,因此无需 shell 转义。【参考方案7】:
import os
cmd = 'ls -al'
os.system(cmd)
如果要返回命令的结果,可以使用os.popen
。但是,自 2.6 版起已弃用此功能,转而支持 subprocess module,其他答案已经很好地涵盖了这一点。
【讨论】:
打开is deprecated 支持subprocess。 您还可以使用 os.system 调用保存结果,因为它的工作方式与 UNIX shell 本身类似,例如 os.system('ls -l > test2.txt')【参考方案8】:有许多不同的库允许您使用 Python 调用外部命令。对于每个库,我都给出了描述并展示了调用外部命令的示例。我用作示例的命令是ls -l
(列出所有文件)。如果您想了解有关我列出的任何库的更多信息,并链接了每个库的文档。
来源
子进程:https://docs.python.org/3.5/library/subprocess.html shlex:https://docs.python.org/3/library/shlex.html 操作系统:https://docs.python.org/3.5/library/os.html sh:https://amoffat.github.io/sh/ 铅垂:https://plumbum.readthedocs.io/en/latest/ 期待:https://pexpect.readthedocs.io/en/stable/ 面料:http://www.fabfile.org/ 特使:https://github.com/kennethreitz/envoy 命令:https://docs.python.org/2/library/commands.html这些都是库
希望这将帮助您决定使用哪个库:)
子进程
子进程允许您调用外部命令并将它们连接到它们的输入/输出/错误管道(stdin、stdout 和 stderr)。子进程是运行命令的默认选择,但有时其他模块更好。
subprocess.run(["ls", "-l"]) # Run command
subprocess.run(["ls", "-l"], stdout=subprocess.PIPE) # This will run the command and return any output
subprocess.run(shlex.split("ls -l")) # You can also use the shlex library to split the command
操作系统
os 用于“操作系统相关功能”。也可以用os.system
和os.popen
调用外部命令(注意:还有一个subprocess.popen)。 os 将始终运行 shell,对于不需要或不知道如何使用 subprocess.run
的人来说是一个简单的替代方案。
os.system("ls -l") # Run command
os.popen("ls -l").read() # This will run the command and return any output
嘘
sh 是一个子进程接口,可让您像调用函数一样调用程序。如果您想多次运行一个命令,这很有用。
sh.ls("-l") # Run command normally
ls_cmd = sh.Command("ls") # Save command as a variable
ls_cmd() # Run command as if it were a function
铅锤
plumbum 是一个用于“类似脚本”的 Python 程序的库。你可以像sh
那样调用函数之类的程序。如果您想在没有外壳的情况下运行管道,Plumbum 很有用。
ls_cmd = plumbum.local("ls -l") # Get command
ls_cmd() # Run command
期待
pexpect 可让您生成子应用程序、控制它们并在其输出中查找模式。对于需要 Unix 上的 tty 的命令,这是子进程的更好替代方案。
pexpect.run("ls -l") # Run command as normal
child = pexpect.spawn('scp foo user@example.com:.') # Spawns child application
child.expect('Password:') # When this is the output
child.sendline('mypassword')
面料
fabric 是 Python 2.5 和 2.7 库。它允许您执行本地和远程 shell 命令。 Fabric 是在安全 shell (SSH) 中运行命令的简单替代方案
fabric.operations.local('ls -l') # Run command as normal
fabric.operations.local('ls -l', capture = True) # Run command and receive output
特使
envoy 被称为“人类的子进程”。它用作subprocess
模块的便捷包装器。
r = envoy.run("ls -l") # Run command
r.std_out # Get output
命令
commands
包含 os.popen
的包装函数,但它已从 Python 3 中删除,因为 subprocess
是更好的选择。
【讨论】:
【参考方案9】:使用标准库
使用subprocess module(Python 3):
import subprocess
subprocess.run(['ls', '-l'])
这是推荐的标准方式。然而,更复杂的任务(管道、输出、输入等)的构建和编写可能会很乏味。
Python 版本注意事项:如果您仍在使用 Python 2,subprocess.call 的工作方式类似。
专业提示:shlex.split 可以帮助您解析run
、call
和其他subprocess
函数的命令,以防您不希望(或者您不能!)提供它们以列表的形式:
import shlex
import subprocess
subprocess.run(shlex.split('ls -l'))
有外部依赖
如果您不介意外部依赖,请使用plumbum:
from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())
这是最好的subprocess
包装器。它是跨平台的,即它适用于 Windows 和类 Unix 系统。通过pip install plumbum
安装。
另一个流行的库是sh:
from sh import ifconfig
print(ifconfig('wlan0'))
然而,sh
放弃了对 Windows 的支持,所以它不像以前那么棒了。通过pip install sh
安装。
【讨论】:
【参考方案10】:我总是使用fabric
来处理这些事情,例如:
from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )
但这似乎是一个不错的工具:sh
(Python subprocess interface)。
看一个例子:
from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)
【讨论】:
【参考方案11】:也检查“pexpect”Python 库。
它允许对外部程序/命令进行交互控制,甚至是 ssh、ftp、telnet 等。您可以输入如下内容:
child = pexpect.spawn('ftp 192.168.0.24')
child.expect('(?i)name .*: ')
child.sendline('anonymous')
child.expect('(?i)password')
【讨论】:
【参考方案12】:如果您需要正在调用的命令的输出, 那么你可以使用subprocess.check_output(Python 2.7+)。
>>> subprocess.check_output(["ls", "-l", "/dev/null"])
'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n'
还要注意shell 参数。
如果shell是
True
,指定的命令将通过shell执行。如果您将 Python 主要用于它在大多数系统 shell 上提供的增强控制流,并且仍然希望方便地访问其他 shell 功能,例如 shell 管道、文件名通配符、环境变量扩展和 ~ 到用户家的扩展,这将很有用目录。但是,请注意 Python 本身提供了许多类似 shell 的功能的实现(特别是glob
、fnmatch
、os.walk()
、os.path.expandvars()
、os.path.expanduser()
和shutil
)。
【讨论】:
请注意,check_output
需要一个列表而不是字符串。如果您不依赖带引号的空格来使您的调用有效,那么最简单、最易读的方法是subprocess.check_output("ls -l /dev/null".split())
。
就像答案模糊地提到,以及此页面上的许多其他答案更详细地解释,您可以传递一个列表,或者使用 shell=True
一个字符串,然后由 shell 负责解析和执行.在您提到的情况下,使用普通的.split()
很好,但初学者通常不了解其中的细微差别;您最好推荐shlex.split()
,它可以正确处理引用和反斜杠转义。【参考方案13】:
更新:
subprocess.run
是推荐的方法as of Python 3.5 如果您的代码不需要保持与早期 Python 版本的兼容性。它更加一致,并提供与 Envoy 类似的易用性。 (不过,管道并不那么简单。请参阅 this question for how。)
以下是来自the documentation 的一些示例。
运行一个进程:
>>> subprocess.run(["ls", "-l"]) # Doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)
运行失败引发:
>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
捕获输出:
>>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')
原答案:
我建议尝试Envoy。它是子进程的包装器,它反过来 aims to replace 旧模块和函数。 Envoy 是人类的子进程。
来自the README的示例用法:
>>> r = envoy.run('git config', data='data to pipe in', timeout=2)
>>> r.status_code
129
>>> r.std_out
'usage: git config [options]'
>>> r.std_err
''
也管东西:
>>> r = envoy.run('uptime | pbcopy')
>>> r.command
'pbcopy'
>>> r.status_code
0
>>> r.history
[<Response 'uptime'>]
【讨论】:
【参考方案14】:这就是我运行命令的方式。这段代码有你需要的一切
from subprocess import Popen, PIPE
cmd = "ls -l ~/"
p = Popen(cmd , shell=True, stdout=PIPE, stderr=PIPE)
out, err = p.communicate()
print "Return code: ", p.returncode
print out.rstrip(), err.rstrip()
【讨论】:
我认为硬编码命令可以接受,如果它增加可读性的话。 解释一下。例如,想法/要点是什么?【参考方案15】:如何从 Python 执行程序或调用系统命令
简单,使用subprocess.run
,它返回一个CompletedProcess
对象:
>>> from subprocess import run
>>> from shlex import split
>>> completed_process = run(split('python --version'))
Python 3.8.8
>>> completed_process
CompletedProcess(args=['python', '--version'], returncode=0)
(run
想要一个词法分析的 shell 参数列表 - 这是你在 shell 中键入的内容,用空格分隔,但不是空格被引用的地方,所以使用专门的函数 split
来拆分你在 shell 中输入的内容)
为什么?
从 Python 3.5 开始,文档推荐subprocess.run:
调用子流程的推荐方法是对它可以处理的所有用例使用 run() 函数。对于更高级的用例,可以直接使用底层的 Popen 接口。
这是一个最简单的用法示例 - 它完全按照要求进行:
>>> from subprocess import run
>>> from shlex import split
>>> completed_process = run(split('python --version'))
Python 3.8.8
>>> completed_process
CompletedProcess(args=['python', '--version'], returncode=0)
run
等待命令成功完成,然后返回一个CompletedProcess
对象。它可能会引发TimeoutExpired
(如果你给它一个timeout=
参数)或CalledProcessError
(如果它失败并且你通过check=True
)。
您可能从上面的示例中推断出,默认情况下,stdout 和 stderr 都会通过管道传输到您自己的 stdout 和 stderr。
我们可以检查返回的对象并查看给出的命令和返回码:
>>> completed_process.args
['python', '--version']
>>> completed_process.returncode
0
捕获输出
如果要捕获输出,可以将subprocess.PIPE
传递给相应的stderr
或stdout
:
>>> from subprocess import PIPE
>>> completed_process = run(shlex.split('python --version'), stdout=PIPE, stderr=PIPE)
>>> completed_process.stdout
b'Python 3.8.8\n'
>>> completed_process.stderr
b''
并且那些各自的属性返回字节。
传递一个命令列表
人们可能很容易从手动提供命令字符串(如问题所暗示的那样)转向提供以编程方式构建的字符串。 不要以编程方式构建字符串。这是一个潜在的安全问题。最好假设您不信任输入。
>>> import textwrap
>>> args = ['python', textwrap.__file__]
>>> cp = run(args, stdout=subprocess.PIPE)
>>> cp.stdout
b'Hello there.\n This is indented.\n'
注意,只有args
应该按位置传递。
完整签名
这是源代码中的实际签名,如help(run)
所示:
def run(*popenargs, input=None, timeout=None, check=False, **kwargs):
popenargs
和 kwargs
被赋予 Popen
构造函数。 input
可以是一串字节(或 unicode,如果指定编码或 universal_newlines=True
),将通过管道传输到子进程的标准输入。
文档对timeout=
和check=True
的描述比我想象的要好:
超时参数被传递给 Popen.communicate()。如果超时 过期,子进程将被杀死并等待。这 TimeoutExpired 异常将在子进程完成后重新引发 终止。
如果检查为真,并且进程以非零退出代码退出,则 将引发 CalledProcessError 异常。那的属性 异常保存参数、退出代码以及 stdout 和 stderr if 他们被俘虏了。
check=True
的这个例子比我想出的要好:
>>> subprocess.run("exit 1", shell=True, check=True) Traceback (most recent call last): ... subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
扩展签名
这是文档中给出的扩展签名:
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None)
请注意,这表明只有 args 列表应按位置传递。所以将剩余的参数作为关键字参数传递。
打开
何时改用Popen
?我很难仅根据论点找到用例。但是,直接使用 Popen
可以让您访问其方法,包括 poll
、'send_signal'、'terminate' 和 'wait'。
这是the source 中给出的Popen
签名。我认为这是对信息最精确的封装(相对于help(Popen)
):
def __init__(self, args, bufsize=-1, executable=None,
stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=True,
shell=False, cwd=None, env=None, universal_newlines=None,
startupinfo=None, creationflags=0,
restore_signals=True, start_new_session=False,
pass_fds=(), *, user=None, group=None, extra_groups=None,
encoding=None, errors=None, text=None, umask=-1, pipesize=-1):
但信息量更大的是the Popen
documentation:
subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=None, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, group=None, extra_groups=None, user=None, umask=-1, encoding=None, errors=None, text=None)
在新进程中执行子程序。在 POSIX 上,该类使用 os.execvp()-like 行为来执行子程序。在 Windows 上, 该类使用 Windows CreateProcess() 函数。论据 Popen如下。
了解Popen
上的其余文档将留给读者作为练习。
【讨论】:
主进程和子进程之间双向通信的简单示例可以在这里找到:***.com/a/52841475/1349673【参考方案16】:使用subprocess。
...或者对于一个非常简单的命令:
import os
os.system('cat testfile')
【讨论】:
【参考方案17】:os.system
还可以,但有点过时了。它也不是很安全。相反,请尝试subprocess
。 subprocess
不直接调用 sh,因此比 os.system
更安全。
获取更多信息here。
【讨论】:
虽然我同意总体建议,但subprocess
并没有消除所有的安全问题,而且它本身也存在一些令人讨厌的问题。【参考方案18】:
还有Plumbum
>>> from plumbum import local
>>> ls = local["ls"]
>>> ls
LocalCommand(<LocalPath /bin/ls>)
>>> ls()
u'build.py\ndist\ndocs\nLICENSE\nplumbum\nREADME.rst\nsetup.py\ntests\ntodo.txt\n'
>>> notepad = local["c:\\windows\\notepad.exe"]
>>> notepad() # Notepad window pops up
u'' # Notepad window is closed by user, command returns
【讨论】:
解释一下。【参考方案19】:从 2018 年 6 月 27 日发布的 Python 3.7.0 (https://docs.python.org/3/whatsnew/3.7.html)开始,您可以以最强大但同样简单的方式实现您想要的结果。该答案旨在以简短的方式向您展示各种选项的基本摘要。如需深入解答,请参阅其他答案。
TL;2021 年 DR
os.system(...)
的最大优势在于其简单性。 subprocess
更好并且仍然易于使用,尤其是从 Python 3.5 开始。
import subprocess
subprocess.run("ls -a", shell=True)
注意:这是您问题的确切答案 - 运行命令
像在贝壳里
首选方式
如果可能,移除 shell 开销并直接运行命令(需要列表)。
import subprocess
subprocess.run(["help"])
subprocess.run(["ls", "-a"])
在列表中传递程序参数。 不要将\"
-escaping 用于包含空格的参数。
高级用例
检查输出
以下代码不言自明:
import subprocess
result = subprocess.run(["ls", "-a"], capture_output=True, text=True)
if "***-logo.png" in result.stdout:
print("You're a fan!")
else:
print("You're not a fan?")
result.stdout
是所有正常的程序输出不包括错误。阅读result.stderr
获取它们。
capture_output=True
- 打开捕获。否则result.stderr
和result.stdout
将是None
。可从 Python 3.7 获得。
text=True
- Python 3.7 中添加的便利参数,可将接收到的二进制数据转换为您可以轻松使用的 Python 字符串。
检查返回码
做
if result.returncode == 127: print("The program failed for some weird reason")
elif result.returncode == 0: print("The program succeeded")
else: print("The program failed unexpectedly")
如果只想检查程序是否成功(returncode == 0),否则抛出异常,有一个更方便的函数:
result.check_returncode()
但它是 Python,所以有一个更方便的参数 check
会自动为你做同样的事情:
result = subprocess.run(..., check=True)
stderr 应该在标准输出里面
您可能希望在标准输出中包含所有程序输出,甚至是错误。为此,请运行
result = subprocess.run(..., stderr=subprocess.STDOUT)
result.stderr
将成为 None
,result.stdout
将包含所有内容。
使用带有参数字符串的 shell=False
shell=False
需要一个 list 参数。但是,您可以使用 shlex 自行拆分参数字符串。
import subprocess
import shlex
subprocess.run(shlex.split("ls -a"))
就是这样。
常见问题
当您遇到这个问题时,很有可能您刚开始使用 Python。让我们来看看一些常见的问题。
FileNotFoundError: [Errno 2] 没有这样的文件或目录:'ls -a': 'ls -a'
您正在运行一个没有 shell=True
的子进程。使用列表 (["ls", "-a"]
) 或设置 shell=True
。
TypeError: [...] NoneType [...]
检查您是否设置了capture_output=True
。
TypeError:需要一个类似字节的对象,而不是 [...]
您总是从您的程序中收到字节结果。如果您想像普通字符串一样使用它,请设置text=True
。
subprocess.CalledProcessError: Command '[...]' 返回非零退出状态 1。
您的命令没有成功运行。您可以禁用返回码检查或检查您的实际程序的有效性。
TypeError: init() 得到了一个意外的关键字参数 [...]
您可能使用的 Python 版本可能早于 3.7.0;将其更新为可用的最新版本。否则,此 Stack Overflow 帖子中还有其他答案,向您展示了较旧的替代解决方案。
【讨论】:
“os.system(...) 的最大优势在于它的简单性。子进程更好” - 子进程如何更好?我很高兴使用 os.system,不知道切换到子进程和记住额外的shell=True
对我有什么好处。子流程中什么样的东西比较好?
您说得对,就简单的“盲”执行而言,os.system(...)
是执行命令的合理选择。然而,用例是相当有限的——一旦你想捕获输出,你必须使用一个完整的其他库,然后你就开始在代码中同时使用子进程和操作系统来处理类似的用例。我更喜欢保持代码干净并只使用其中一个。其次,我会将该部分放在顶部,但 TL;DR 必须确切地回答这个问题,您应该不使用shell=True
,而是使用我的'已经写在Preferred Way
部分。
os.system(...)
和shell=True
的问题是你正在生成一个新的shell 进程,只是为了执行你的命令。这意味着,您必须进行手动转义,这并不像您想象的那么简单——尤其是在同时针对 POSIX 和 Windows 时。对于用户提供的输入,这是不行的(想象一下用户用"
引号输入了一些东西——你也必须转义它们)。此外,shell 进程本身可能会加载您不需要的代码 - 它不仅会延迟程序,而且还可能导致意外的副作用,以错误的返回码结束。
总结一下,os.system(...)
确实可以使用。但是,一旦您编写的不仅仅是一个快速的 python 帮助脚本,我建议您在没有shell=True
的情况下使用 subprocess.run。有关 os.system 缺点的更多信息,我想建议您阅读这个 SO 答案:***.com/a/44731082/6685358
谢谢!我想编辑“更好”以包含该链接,但我收到有关完整编辑队列的错误。【参考方案20】:
用途:
import os
cmd = 'ls -al'
os.system(cmd)
os - 此模块提供了一种使用操作系统相关功能的可移植方式。
对于更多os
函数,here 是文档。
【讨论】:
它也被弃用了。使用子进程【参考方案21】:可以这么简单:
import os
cmd = "your command"
os.system(cmd)
【讨论】:
这没有指出缺点,PEP-324 中有更详细的解释。os.system
的文档明确建议避免使用它以支持 subprocess
。【参考方案22】:
这里还有一个之前没有提到的区别。
subprocess.Popen
将
我尝试了子进程,执行成功。但是 无法与 通信。 当我从终端运行时一切正常。
还有一个: (注意:kwrite 的行为与其他应用程序不同。如果您使用 Firefox 尝试以下操作,结果将不一样。)
如果您尝试os.system("kwrite")
,程序流程将冻结,直到用户关闭 kwrite。为了克服这个问题,我尝试了os.system(konsole -e kwrite)
。这次程序继续流动,但是kwrite变成了控制台的子进程。
任何运行 kwrite 的人都不是子进程(即在系统监视器中,它必须出现在树的最左边)。
【讨论】:
“任何人都运行 kwrite 而不是子进程”是什么意思?subprocess
运行子进程确实令人费解。【参考方案23】:
os.system
不允许您存储结果,因此如果您想将结果存储在某个列表或其他内容中,subprocess.call
可以。
【讨论】:
【参考方案24】:我倾向于将subprocess 与shlex 一起使用(处理引号字符串的转义):
>>> import subprocess, shlex
>>> command = 'ls -l "/your/path/with spaces/"'
>>> call_params = shlex.split(command)
>>> print call_params
["ls", "-l", "/your/path/with spaces/"]
>>> subprocess.call(call_params)
【讨论】:
【参考方案25】:subprocess.check_call
如果您不想测试返回值,则很方便。任何错误都会引发异常。
【讨论】:
【参考方案26】:我为此编写了一个库,shell.py。
它现在基本上是 popen 和 shlex 的包装器。它还支持管道命令,因此您可以在 Python 中更轻松地链接命令。因此,您可以执行以下操作:
ex('echo hello shell.py') | "awk 'print $2'"
【讨论】:
【参考方案27】:在 Windows 中,您只需导入 subprocess
模块并通过调用 subprocess.Popen()
、subprocess.Popen().communicate()
和 subprocess.Popen().wait()
来运行外部命令,如下所示:
# Python script to run a command line
import subprocess
def execute(cmd):
"""
Purpose : To execute a command and return exit status
Argument : cmd - command to execute
Return : exit_code
"""
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(result, error) = process.communicate()
rc = process.wait()
if rc != 0:
print "Error: failed to execute command:", cmd
print error
return result
# def
command = "tasklist | grep python"
print "This process detail: \n", execute(command)
输出:
This process detail:
python.exe 604 RDP-Tcp#0 4 5,660 K
【讨论】:
【参考方案28】:在 Linux 下,如果您想调用将独立执行的外部命令(将在 Python 脚本终止后继续运行),您可以使用简单的队列作为 task spooler 或 at 命令。
任务假脱机示例:
import os
os.system('ts <your-command>')
关于任务后台处理程序 (ts
) 的说明:
您可以设置要运行的并发进程数(“槽”):
ts -S <number-of-slots>
安装ts
不需要管理员权限。您可以使用简单的make
从源代码下载并编译它,将其添加到您的路径中即可。
【讨论】:
ts
在我所知道的任何发行版中都不是标准的,尽管指向at
的指针有点用处。您可能还应该提到batch
。与其他地方一样,os.system()
建议至少应该提到subprocess
是它的推荐替代品。【参考方案29】:
Invoke 是一个 Python(2.7 和 3.4+)任务执行工具和库。它为运行 shell 命令提供了一个干净的高级 API:
>>> from invoke import run
>>> cmd = "pip install -r requirements.txt"
>>> result = run(cmd, hide=True, warn=True)
>>> print(result.ok)
True
>>> print(result.stdout.splitlines()[-1])
Successfully installed invocations-0.13.0 pep8-1.5.7 spec-1.3.1
【讨论】:
这是一个很棒的图书馆。前几天我试图向同事解释它并这样描述它:invoke
是 subprocess
就像 requests
是 urllib3
。【参考方案30】:
您可以使用Popen,然后您可以检查程序的状态:
from subprocess import Popen
proc = Popen(['ls', '-l'])
if proc.poll() is None:
proc.kill()
查看subprocess.Popen。
【讨论】:
以上是关于如何执行程序或调用系统命令?的主要内容,如果未能解决你的问题,请参考以下文章
怎样在一个 C 程序中调用另一个程序 (独立可执行的程序, 或系统命令)?
C或C++如何通过程序执行shell命令并获取命令执行结果?