如何在Python中调用外部命令,就像在shell或命令提示符中键入它一样?
如何在Python中调用外部命令,就像在shell或命令提示符中键入它一样?
subprocess.run
:import subprocess
subprocess.run(["ls", "-l"])
os.system
,但你不应该使用它,因为如果命令的任何部分来自程序外部或可能包含空格或其他特殊字符,则会存在安全隐患。此外,subprocess.run
通常更加灵活(你可以获取stdout
、stderr
、"真实"状态码、更好的错误处理等)。即使os.system
的文档也建议使用subprocess
代替。subprocess.call
而不是.run
:subprocess.call(["ls", "-l"])
以下是调用外部程序的方法总结,包括它们的优缺点:
os.system
将命令和参数传递给系统的shell。这很好,因为您可以以这种方式运行多个命令,并设置管道和输入/输出重定向。例如:
os.system("some_command < input_file | another_command > output_file")
然而,虽然这很方便,但你需要手动处理shell字符的转义,例如空格等。另一方面,这也让你运行仅仅是shell命令而不是实际外部程序的命令。
os.popen
和os.system
一样,但它给了你一个类似文件的对象,可以用来访问该进程的标准输入/输出。有三种其他变体的popen,它们都略微处理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函数中确实很不错。详见文档。
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 /
”的内容,这样做可能会删除整个文件系统。
open
。这个回答并没有直接回答文章中Python口号的合理性,因此需要进一步解释。Python的口号是"优美胜于丑陋,明了胜于晦涩,简洁胜于复杂",它强调代码的可读性和易用性。与Perl不同,Python鼓励开发者遵循一种主要的编程方式,在减少错误和提高代码可读性方面具有优势。虽然Perl也有其独特的优点,但它的灵活性和允许多种编程方式的特点可能会导致代码难以维护。 - Jeansubprocess.run()
。https://docs.python.org/3.5/library/subprocess.html#subprocess.run - phoenixsubprocess.run(...)
,默认情况下,“这不会捕获stdout或stderr。”的含义是什么?对于subprocess.check_output(...)
和STDERR呢? - Evgeni Sergeevsubprocess.run()
或者它的老版本subprocess.check_call()
等等方法。对于这些方法不能满足需求的情况,可以考虑使用subprocess.Popen()
。也许应该避免提及os.popen()
,或者将其放在“自己编写fork/exec/spawn代码”之后。 - tripleee典型实现:
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,
- jfsp.stdout.readline()
(注意:末尾没有s
)在子进程填满其缓冲区之前不会看到任何数据。如果子进程没有产生太多数据,则输出将不会实时显示。请参见 Q: Why not just use a pipe (popen())? 中的第二个原因。一些解决方法可以在 此答案 中找到(例如 pexpect、pty、stdbuf)。 - jfsPopen
来执行简单任务了。这也没有必要指定shell=True
参数。可以尝试使用subprocess.run()
方法中的一个答案。 - tripleee关于如何将子进程与调用者进程分离的一些提示(在后台启动子进程)。
假设您想从CGI脚本中开始一个长时间运行的任务。即,子进程应该比CGI脚本执行进程存活更长时间。
subprocess模块文档中的经典示例为:
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进程创建标志传递给Windows API的底层CreateProcess函数。如果您已安装pywin32,则可以从win32process模块导入该标志,否则您应该自己定义它:
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中启动后台进程的Google搜索并没有帮助解决问题。
import subprocess as sp;sp.Popen('calc')
没有等待子进程完成。似乎 creationflags 不是必要的。我错过了什么? - ubershmekelCREATE_NEW_CONSOLE
(0x00000010)。 - Eryk Sunos.devnull
。当您希望子进程与父进程同时与用户交互时,请创建一个新的控制台。在单个窗口中同时进行这两个操作可能会令人困惑。 - Eryk Sunimport os
os.system("your command")
os
}}和 {{link2:sys
}} 模块的文档。存在许多类似功能的函数({{link3:exec*
}}和{{link4:spawn*
}})。subprocess
,这是一个稍微更加通用和可移植的解决方案。运行外部命令本质上是不可移植的(您必须确保命令在您需要支持的每个架构上都可用),并且将用户输入作为外部命令传递本质上是不安全的。 - tripleee我建议使用 subprocess 模块来代替 os.system,因为它可以为您进行 shell 转义,因此更加安全。
subprocess.call(['ping', 'localhost'])
shell=False
时与subprocess
一起使用,那么可以使用shlex.split
来轻松完成此操作。https://docs.python.org/2/library/shlex.html#shlex.split(根据文档,这是推荐的方法 https://docs.python.org/2/library/subprocess.html#popen-constructor) - Daniel Fimport os
cmd = 'ls -al'
os.system(cmd)
如果您想返回命令的结果,可以使用os.popen
。但是,自2.6版本起,它已被subprocess模块取代,其他答案已经很好地涵盖了此模块。
有很多不同的库可以让你在Python中调用外部命令。对于每个库,我都给出了描述,并展示了调用外部命令的示例。我使用的命令示例是ls -l
(列出所有文件)。如果您想了解更多关于我列出的任何库的信息,我已经为每个库链接了文档。
希望这能帮助您决定使用哪个库 :)
Subprocess允许您调用外部命令并将其连接到它们的输入/输出/错误管道(stdin、stdout和stderr)。 Subprocess是运行命令的默认选择,但有时其他模块更好。
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
中那样调用程序作为函数来运行。如果你想要在没有shell的情况下运行一个管道,Plumbum就非常有用。
ls_cmd = plumbum.local("ls -l") # Get command
ls_cmd() # Run command
pexpect可以启动子应用程序、控制它们并查找它们的输出模式。对于在Unix上需要tty的命令,这是subprocess的一个更好的替代方案。
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')
面料是一个适用于Python 2.5和2.7的库。它允许您执行本地和远程shell命令。面料是在安全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)”。它是subprocess
模块的便捷包装器。
r = envoy.run("ls -l") # Run command
r.std_out # Get output
commands
包含了对 os.popen
的包装函数,但自从出现更好的替代品 subprocess
后,在 Python 3 中已被移除。
ls_cmd = plumbum.local["ls -l"]
# 获取命令 - innisfree使用 subprocess 模块(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
。
我通常使用fabric
来完成这些任务。以下是演示代码:
from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )
但这似乎是一个好工具:sh
(Python子进程接口)。
看一个例子:
from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)
call(["echo", "$PATH"])
来执行echo $PATH
,但它只是回显了字面字符串$PATH
而没有做任何替换。我知道可以获取PATH环境变量,但我想知道是否有一种简单的方法,使命令的行为与在bash中执行它的行为完全相同。 - Kevin Wheelershell=True
才能让它工作。 - SethMMortonshell=True
,Python中有 os.path.expandvars 函数可以实现同样的功能。在你的情况下,你可以这样写:os.path.expandvars("$PATH")
。@SethMMorton 请重新考虑你的评论 -> 为什么不要使用 shell=True。 - Murmelpip list | grep anatome
,该怎么办? - Charlie Parker