在Python中调用外部命令
如何从Python脚本中调用一个外部命令(就像我在Unix shell或Windows命令提示符下键入的那样)?
查看标准库中的subprocess模块 :
from subprocess import call call(["ls", "-l"])
子系统和系统的优点是它更加灵活(你可以得到stdout,stderr,“真正的”状态码,更好的error handling等等)。
官方文档build议使用替代os.system()的子进程模块:
子stream程模块提供了更多强大的工具来产生新的stream程和检索结果; 使用这个模块比使用这个函数[
os.system()
]更os.system()
。
子stream程文档中的“ 使用子stream程模块replace旧function ”部分可能有一些有用的配方。
有关子stream程模块的官方文档:
- Python 2 – subprocess
- Python 3 – subprocess
以下是调用外部程序的方法以及每种方法的优点和缺点:
-
os.system("some_command with args")
将命令和parameter passing到系统的shell。 这很好,因为你可以用这种方式一次运行多个命令,并设置pipe道和input/输出redirect。 例如:os.system("some_command < input_file | another_command > output_file")
然而,虽然这很方便,但您必须手动处理空格等shell字符的转义。另一方面,这也可以让您运行仅仅是shell命令的命令,而不是实际上的外部程序。 请参阅文档 。
-
stream = os.popen("some_command with args")
会和os.system
做同样的事情,只不过它提供了一个类似于文件的对象,可以用来访问该进程的标准input/输出。 还有另外3种不同的popen,它们的处理方式略有不同。 如果你把所有东西都作为一个string传递,那么你的命令就被传递给shell; 如果你把它们作为一个列表,那么你不需要担心逃脱任何事情。 请参阅文档 。 -
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)
请参阅文档 。
-
如果您使用的是Python 3.5或更高版本,则可以使用新的
subprocess.run
函数,该函数与上述类似,但更为灵活,并在命令执行CompletedProcess
时返回CompletedProcess
对象。 -
OS模块也具有C程序中所有的fork / exec / spawn函数,但我不build议直接使用它们。
subprocess
模块可能应该是你使用的。
最后,请注意,对于所有将shell以string的forms传递给最终命令的方法,您有责任将其转义。 如果您传递的string的任何部分不能完全信任,则存在严重的安全隐患 。 例如,如果用户正在inputstring的某个/任何部分。 如果您不确定,只能使用这些常量的方法。 给你一个暗示的提示考虑这个代码:
print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()
并想象用户input“我的妈妈不爱我&& rm -rf /”。
我通常使用:
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()
您可以自由地使用pipe道中的stdout
数据执行所需的操作。 事实上,你可以简单地省略这些参数( stdout=
和stderr=
),它的行为就像os.system()
。
一些提示将subprocess从调用者中分离出来(在后台启动subprocess)。
假设你想从一个CGI脚本开始一个长时间的任务,那就是subprocess应该比CGI脚本的执行过程长。
子stream程模块文档中的经典示例是:
import subprocess import sys # some code here pid = subprocess.Popen([sys.executable, "longtask.py"]) # call subprocess # some more code here
这里的想法是,你不想在“调用subprocess”的行中等待longtask.py完成。 但是不清楚在这个例子中“这里有更多的代码”之后会发生什么。
我的目标平台是freebsd,但开发是在Windows上,所以我首先面对Windows的问题。
在Windows(win xp)上,父进程将不会完成,直到longtask.py完成其工作。 这不是你想要的CGI脚本。 这个问题不是特定于Python,在PHP社区中的问题是一样的。
解决方法是在win API中将DETACHED_PROCESS 进程创build标志传递给底层的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上我们还有另外一个问题:当父进程完成时,它也完成subprocess。 这不是你想要的CGI脚本。 一些实验表明,这个问题似乎在共享sys.stdout。 工作解决scheme如下:
pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
我没有检查过其他平台上的代码,也不知道freebsd上的行为的原因。 如果有人知道,请分享你的想法。 在Python中开始后台进程的search没有任何亮点。
我build议使用subprocess模块,而不是os.system,因为它确实shell逃脱你,因此更安全: http ://docs.python.org/library/subprocess.html
subprocess.call(['ping', 'localhost'])
import os cmd = 'ls -al' os.system(cmd)
如果你想返回命令的结果,你可以使用os.popen
。 然而,从版本2.6开始,这被弃用,而是支持子stream程模块 ,其他的答案已经被覆盖了。
import os os.system("your command")
请注意,这是危险的,因为该命令没有清理。 我把它放在你的谷歌'os'和'sys'模块的相关文档。 有一堆函数(exec *,spawn *)会做类似的事情。
检查“pexpect”Python库。
它允许外部程序/命令的交互式控制,甚至ssh,ftp,telnet等。你可以input如下内容:
child = pexpect.spawn('ftp 192.168.0.24') child.expect('(?i)name .*: ') child.sendline('anonymous') child.expect('(?i)password')
我总是使用fabric
这样的事情:
from fabric.operations import local result = local('ls', capture=True) print "Content:/n%s" % (result, )
但是这似乎是一个很好的工具: sh
(Pythonsubprocess接口) 。
看一个例子:
from sh import vgdisplay print vgdisplay() print vgdisplay('-v') print vgdisplay(v=True)
如果你需要的是你所调用的命令的输出,
那么你可以使用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中提供的增强控制stream,并且仍希望方便地访问其他shellfunction(如shellpipe道,文件名通配符,环境variables扩展以及将〜扩展到用户的home目录。 但是,请注意,Python本身提供了许多类似shell的特性(特别是glob
,fnmatch
,os.walk()
,os.path.expandvars()
,os.path.expanduser()
和shutil
)的实现。
有很多不同的库允许你用Python调用外部命令。 对于每个库,我都给出了一个描述,并给出了一个调用外部命令的例子。 我用作例子的命令是ls -l
(列出所有文件)。 如果您想了解更多关于我列出的任何库的信息,并链接了每个库的文档。
资料来源:
- subprocess: https : //docs.python.org/3.5/library/subprocess.html
- shlex: https ://docs.python.org/3/library/shlex.html
- os: https : //docs.python.org/3.5/library/os.html
- sh: https : //amoffat.github.io/sh/
- 铅: https : //plumbum.readthedocs.io/en/latest/
- pexpect: https ://pexpect.readthedocs.io/en/stable/
- 面料: http : //www.fabfile.org/
- 特使: https : //github.com/kennethreitz/envoy
- 命令: https : //docs.python.org/2/library/commands.html
这些都是图书馆:
希望这会帮助你决定使用哪个库:)
子
subprocess允许你调用外部命令,并将它们连接到它们的input/输出/错误pipe道(stdin,stdout和stderr)。 subprocess是运行命令的默认select,但有时其他模块更好。
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用于“依赖于操作系统的function”。 它也可以用来调用os.system
和os.popen
外部命令(注意:还有一个os.popen
)。 操作系统将永远运行的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是一个subprocess接口,它可以让你像调用函数一样调用程序。 如果要多次运行命令,这非常有用。
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的情况下运行pipe道,则铅是非常有用的。
ls_cmd = plumbum.local("ls -l") # get command ls_cmd() # run command
Pexpect的
pexpect让你产生子应用程序,控制它们并在其输出中find模式。 对于期望在Unix上使用tty的命令,这是对subprocess的更好的select。
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命令。 结构是在安全shell(SSH)中运行命令的简单替代方法
fabric.operations.local('ls -l') # Run command as normal fabric.operations.local('ls -l', capture = True) # Run command and receive output
使者
特使被称为“人类的子过程”。 它用作subprocess
模块的便利包装。
r = envoy.run("ls -l") # Run command r.std_out # get output
命令
commands
包含os.popen
包装函数,但是它已经从Python 3中删除了,因为subprocess
是一个更好的select。
编辑基于JF Sebastian的评论。
这是我如何运行我的命令。 这个代码有你需要的一切
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()
用标准库
使用子stream程模块 :
from subprocess import call call(['ls', '-l'])
这是推荐的标准方式。 然而,更复杂的任务(pipe道,输出,input等)可能是枯燥的构造和写作。
注: shlex.split可以帮助你parsingcall
和其他subprocess
进程函数的命令,以防万一你不想(或者你不能)以列表的forms提供它们:
import shlex from subprocess import call call(shlex.split('ls -l'))
与外部依赖关系
如果你不介意外部依赖,使用铅 :
from plumbum.cmd import ifconfig print(ifconfig['wlan0']())
这是最好的subprocess
封装。 它是跨平台的,即它可以在Windows和类Unix系统上运行。 通过pip install plumbum
。
另一个stream行的图书馆是sh :
from sh import ifconfig print(ifconfig('wlan0'))
但是, sh
放弃了对Windows的支持,所以它不像以前那么棒。 通过pip install sh
。
更新:
如果您的代码不需要保持与早期Python版本的兼容性,那么在Python 3.5中推荐使用subprocess.run。 它更一致,并提供与Envoy类似的易用性。 (pipe道不是那么简单,请看这个问题 。)
以下是文档中的一些示例。
运行一个进程:
>>> 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')
原始答案:
我build议尝试特使 。 这是一个subprocess的包装,它反过来旨在取代旧的模块和function。 特使是人类的subprocess。
自述文件中的示例用法:
>>> 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 ''
pipe东西也是:
>>> r = envoy.run('uptime | pbcopy') >>> r.command 'pbcopy' >>> r.status_code 0 >>> r.history [<Response 'uptime'>]
没有结果的输出:
import os os.system("your command here")
输出结果:
import commands commands.getoutput("your command here") or commands.getstatusoutput("your command here")
还有铅
>>> 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
os.system
是可以的,但有点过时。 这也不是很安全。 相反,尝试subprocess
。 subprocess
os.system
不直接调用sh,因此比os.system
更安全。
在这里获取更多信息。
使用:
import os cmd = 'ls -al' os.system(cmd)
操作系统 – 该模块提供了使用操作系统相关function的便携方式。
更多的os
function, 这里是文档。
如果你不想testing返回值, subprocess.check_call
是很方便的。 它会抛出任何错误的exception。
这里还有一个不同之处,上面没有提到。
subprocess.Popen
执行一个subprocess。 在我的情况下,我需要执行需要与另一个程序进行通信的文件。
我试过子程序,执行成功了。 但是不能通讯w /。 一切正常,当我从terminal运行。
还有一个:(注意:kwrite的行为与其他应用程序不同,如果你在下面尝试firefox的结果将不会是一样的)
如果你尝试os.system("kwrite")
,程序stream程冻结,直到用户closureskwrite。 为了克服这个,我试着改为os.system(konsole -e kwrite)
。 这个时间程序继续stream动,但kwrite成为konsole的subprocess。
任何人运行kwrite不是一个subprocess(即在系统监视器,它必须出现在树的最左边)
os.system
已os.system
进程模块取代。 改用subproccess。
os.system
不允许你存储结果,所以如果你想在一些列表中存储结果或者subprocess.call
工程。
我倾向于与shlex一起使用subprocess (来处理引用string的转义):
>>> 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)
无耻的插件,我为此写了一个库:P https://github.com/houqp/shell.py
它现在基本上是popen和shlex的包装。 它还支持pipe道命令,因此您可以在Python中更轻松地链接命令。 所以你可以做这样的事情:
ex('echo hello shell.py') | "awk '{print $2}'"
你可以使用Popen,然后你可以检查程序的状态:
from subprocess import Popen proc = Popen(['ls', '-l']) if proc.poll() is None: proc.kill()
检查出subprocess.Popen 。
从openstack中子获取networkingID:
#!/usr/bin/python import os netid= "nova net-list | awk '/ External / { print $2 }'" temp=os.popen(netid).read() /* here temp also contains new line (\n) */ networkId=temp.rstrip() print(networkId)
新星网表的输出
+--------------------------------------+------------+------+ | ID | Label | CIDR | +--------------------------------------+------------+------+ | 431c9014-5b5d-4b51-a357-66020ffbb123 | test1 | None | | 27a74fcd-37c0-4789-9414-9531b7e3f126 | External | None | | 5a2712e9-70dc-4b0e-9281-17e02f4684c9 | management | None | | 7aa697f5-0e60-4c15-b4cc-9cb659698512 | Internal | None | +--------------------------------------+------------+------+
打印输出(networkId)
27a74fcd-37c0-4789-9414-9531b7e3f126
在Linux下,如果您想要调用一个独立执行的外部命令(在python脚本终止后将继续运行),您可以使用一个简单的队列作为任务假脱机程序或at命令
任务假脱机程序的示例:
import os os.system('ts <your-command>')
有关任务假脱机程序( ts
)的说明:
-
您可以设置要运行的并发进程数(“槽”):
ts -S <number-of-slots>
-
安装
ts
不需要pipe理员权限。 您可以使用简单的源代码从源代码下载并编译它,然后将其添加到您的path中即可完成。
这是我的两分钱:在我看来,这是处理外部命令时的最佳做法。
这些是执行方法的返回值…
pass, stdout, stderr = execute(["ls","-la"],"/home/user/desktop")
这是执行方法…
def execute(cmdArray,workingDir): stdout = '' stderr = '' try: try: process = subprocess.Popen(cmdArray,cwd=workingDir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1) except OSError: return [False, '', 'ERROR : command(' + ' '.join(cmdArray) + ') could not get executed!'] for line in iter(process.stdout.readline, b''): try: echoLine = line.decode("utf-8") except: echoLine = str(line) stdout += echoLine for line in iter(process.stderr.readline, b''): try: echoLine = line.decode("utf-8") except: echoLine = str(line) stderr += echoLine except (KeyboardInterrupt,SystemExit) as err: return [False,'',str(err)] process.stdout.close() returnCode = process.wait() if returnCode != 0 or stderr != '': return [False, stdout, stderr] else: return [True, stdout, stderr]
在Windows中,您可以通过调用subprocess.Popen()
, subprocess.Popen().communicate()
和subprocess.Popen().wait()
来导入subprocess
模块并运行外部命令,如下所示:
# 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