在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

以下是调用外部程序的方法以及每种方法的优点和缺点:

  1. os.system("some_command with args")将命令和parameter passing到系统的shell。 这很好,因为你可以用这种方式一次运行多个命令,并设置pipe道和input/输出redirect。 例如:

     os.system("some_command < input_file | another_command > output_file") 

    然而,虽然这很方便,但您必须手动处理空格等shell字符的转义。另一方面,这也可以让您运行仅仅是shell命令的命令,而不是实际上的外部程序。 请参阅文档 。

  2. stream = os.popen("some_command with args")会和os.system做同样的事情,只不过它提供了一个类似于文件的对象,可以用来访问该进程的标准input/输出。 还有另外3种不同的popen,它们的处理方式略有不同。 如果你把所有东西都作为一个string传递,那么你的命令就被传递给shell; 如果你把它们作为一个列表,那么你不需要担心逃脱任何事情。 请参阅文档 。

  3. 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函数是很好的。 请参阅文档 。

  4. 来自subprocess模块的call函数。 这基本上就像Popen类一样,并且采用了所有相同的参数,但是它只是等待命令完成并给出返回码。 例如:

     return_code = subprocess.call("echo Hello World", shell=True) 

    请参阅文档 。

  5. 如果您使用的是Python 3.5或更高版本,则可以使用新的subprocess.run函数,该函数与上述类似,但更为灵活,并在命令执行CompletedProcess时返回CompletedProcess对象。

  6. 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的特性(特别是globfnmatchos.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.systemos.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 

https://docs.python.org/2/library/subprocess.html

…或者一个非常简单的命令:

 import os os.system('cat testfile') 

os.system是可以的,但有点过时。 这也不是很安全。 相反,尝试subprocesssubprocess os.system不直接调用sh,因此比os.system更安全。

在这里获取更多信息。

使用:

 import os cmd = 'ls -al' os.system(cmd) 

操作系统 – 该模块提供了使用操作系统相关function的便携方式。

更多的osfunction, 这里是文档。

如果你不想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.systemos.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 )的说明:

  1. 您可以设置要运行的并发进程数(“槽”):

    ts -S <number-of-slots>

  2. 安装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