您如何在Python脚本中调用外部命令(就像我在Unix Shell或Windows命令提示符下键入的一样)?
从Python调用外部命令
在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
subprocess.check_call
如果您不想测试返回值,则非常方便。任何错误都会引发异常。
我很喜欢shell_command的简单性。它建立在子流程模块的顶部。
这是文档中的示例:
>>> from shell_command import shell_call
>>> shell_call("ls *.py")
setup.py shell_command.py test_shell_command.py
0
>>> shell_call("ls -l *.py")
-rw-r--r-- 1 ncoghlan ncoghlan 391 2011-12-11 12:07 setup.py
-rw-r--r-- 1 ncoghlan ncoghlan 7855 2011-12-11 16:16 shell_command.py
-rwxr-xr-x 1 ncoghlan ncoghlan 8463 2011-12-11 16:17 test_shell_command.py
0
os.system
不允许您存储结果,因此,如果要将结果存储在某个列表或某些内容中,则subprocess.call
可以使用。
这里还有另一个以前没有提到的区别。
subprocess.Popen
将<command>作为子进程执行。就我而言,我需要执行文件<a>,该文件需要与另一个程序<b>通信。
我尝试了子流程,执行成功。但是<b>无法与<a>通信。当我从终端运行时,一切都正常。
还有一个:(注意:kwrite的行为与其他应用程序不同。如果在Firefox上尝试以下操作,结果将有所不同。)
如果尝试这样做os.system("kwrite")
,程序流将冻结,直到用户关闭kwrite。为了克服这个问题,我改为尝试os.system(konsole -e kwrite)
。这个时间程序继续进行,但是kwrite成为了控制台的子进程。
任何人都运行kwrite而不是子进程(即,它必须在系统监视器中显示在树的最左侧)。
下面总结了调用外部程序的方法以及每种方法的优缺点:
os.system("some_command with args")
将命令和参数传递到系统的外壳程序。很好,因为您实际上可以以这种方式一次运行多个命令,并设置管道和输入/输出重定向。例如:os.system("some_command < input_file | another_command > output_file")
但是,尽管这很方便,但是您必须手动处理外壳字符(例如空格等)的转义。另一方面,这还允许您运行仅是外壳命令而不是外部程序的命令。请参阅文档。
stream = os.popen("some_command with args")
os.system
除了会为您提供类似于文件的对象之外,您可以使用该对象来访问该过程的标准输入/输出,它的作用与之相同。popen还有其他3种变体,它们对I / O的处理略有不同。如果您将所有内容都作为字符串传递,那么您的命令将传递到外壳程序;如果将它们作为列表传递,则无需担心转义任何内容。请参阅文档。模块的
Popen
类subprocess
。它旨在替代它,os.popen
但缺点是由于太全面而使它稍微复杂一些。例如,您会说:print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()
代替:
print os.popen("echo Hello World").read()
但是将所有选项都放在一个统一的类中而不是4个不同的popen函数是一个很好的选择。请参阅文档。
call
来自subprocess
模块的功能。基本上,这和Popen
该类一样,并使用所有相同的参数,但是它只是等待命令完成并提供返回代码。例如:return_code = subprocess.call("echo Hello World", shell=True)
请参阅文档。
如果您使用的是Python 3.5或更高版本,则可以使用新
subprocess.run
函数,该函数与上面的代码非常相似,但是更加灵活,并CompletedProcess
在命令完成执行后返回一个对象。os模块还具有您在C程序中拥有的所有fork / exec / spawn函数,但是我不建议直接使用它们。
该subprocess
模块可能是您所使用的模块。
最后,请注意,对于所有方法,在这些方法中,您将要由外壳程序执行的最终命令作为字符串传递给您,并且您有责任对其进行转义。如果您传递的字符串的任何部分不能被完全信任,则将带来严重的安全隐患。例如,如果用户正在输入字符串的某些/任何部分。如果不确定,请仅将这些方法与常量一起使用。为了给您暗示的含义,请考虑以下代码:
print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()
并想象用户输入了“我的妈妈不爱我&& rm -rf /”这样的东西,它可能会擦除整个文件系统。
可能很简单:
import os
cmd = "your command"
os.system(cmd)
os.system
可以,但是有点过时。这也不是很安全。相反,请尝试subprocess
。 subprocess
不会直接调用sh,因此比os.system
。
在此处获取更多信息。
这就是我运行命令的方式。这段代码包含了您非常需要的所有内容
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()
关于从调用者中分离子进程的一些提示(在后台启动子进程)。
假设您要从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 进程创建标志传递给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中启动后台进程尚未透露任何信息。
import os
os.system("your command")
请注意,这很危险,因为未清除命令。我留给你去谷歌搜索有关“操作系统”和“系统”模块的相关文档。有很多函数(exec *和spawn *)将执行类似的操作。
查看标准库中的子流程模块:
的优势
subprocess
主场迎战system
的是,它是更灵活(你可以得到的stdout
,stderr
,“真正”的状态代码,更好的错误处理,等等)。在官方文件建议
subprocess
在替代模块os.system()
:在与子模块更换旧的功能中部分
subprocess
文件可能有一些有益的食谱。对于3.5之前的Python版本,请使用
call
: