提问



如何在Python脚本中调用外部命令(就好像我在Unix shell或Windows命令提示符下键入它一样)?

最佳参考


查看标准库中的子进程模块:[145]


from subprocess import call
call(["ls", "-l"])


子流程系统的优势在于它更灵活(您可以获取stdout,stderr,真实状态代码,更好的错误处理等。 )。


官方文档推荐子进程模块替代os.system():[146]



  子流程模块提供了更强大的工具来生成新流程并检索其结果;使用该模块优于使用此功能[[os.system()]]。[147]



子流程文档中的用子流程模块替换旧函数部分可能有一些有用的配方。[148]


关于子流程模块的官方文档:



  • Python 2 - 子流程

  • Python 3 - 子流程


其它参考1


以下是对外部程序调用方式及各自优缺点的总结:[149] [150]



  1. os.system("some_command with args")将命令和参数传递给系统的shell。这很好,因为你实际上可以用这种方式一次运行多个命令并设置管道和输入/输出重定向。例如:


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


    但是,虽然这很方便,但您必须手动处理shell字符的转义,例如空格等。另一方面,这也允许您运行只是shell命令而不是外部程序的命令。见文件。[151]

  2. stream = os.popen("some_command with args")将执行与os.system相同的操作,除了它为您提供了一个类似文件的对象,您可以使用它来访问该进程的标准输入/输出。还有3种其他的popen变体,它们对i/o的处理方式略有不同。如果您将所有内容都作为字符串传递,那么您的命令将传递给shell;如果你将它们作为列表传递,那么你不必担心逃避任何事情。请参阅文档。[152]

  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函数中拥有所有选项是很好的。见文件。[153]

  4. subprocess模块的call功能。这基本上就像Popen类一样,并且接受所有相同的参数,但它只是等待命令完成并为您提供返回代码。例如:


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


    见文件。[154]

  5. 如果您使用的是Python 3.5或更高版本,则可以使用新的subprocess.run函数,它与上述功能非常相似,但更灵活,并在命令完成执行时返回CompletedProcess对象[155] [156]

  6. os模块还具有您在C程序中拥有的所有fork/exec/spawn函数,但我不建议直接使用它们。



subprocess模块可能应该是您使用的模块。


最后请注意,对于所有将shell执行的最终命令作为字符串传递的方法,您负责转义它。 如果您传递的字符串的任何部分无法完全信任,则存在严重的安全隐患。例如,如果用户正在输入字符串的某些/任何部分。如果您不确定,请仅将这些方法与常量一起使用。为了给您一些暗示,请考虑以下代码:


print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()


并想象用户输入我的妈妈不爱我&& rm -rf/。

其它参考2


我通常使用:


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()那样表现。

其它参考3


有关将子进程与调用进程分离的一些提示(在后台启动子进程)。


假设您想从CGI脚本启动一个长任务,即子进程应该比CGI脚本执行过程更长寿。


子流程模块docs的经典示例是:


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(win xp)上,父进程将不会完成,直到longtask.py完成其工作。这不是你想要的CGI脚本。问题不是Python特有的,在PHP社区中问题是一样的。


解决方案是将DETACHED_PROCESS流程创建标志传递给win API中的底层CreateProcess函数。
如果你碰巧安装了pywin32,你可以从win32process模块​​导入标志,否则你应该自己定义:[157]


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中启动后台进程尚未解决任何问题。

其它参考4


我建议使用子进程模块而不是os.system,因为它会为你进行shell转义,因此更加安全:http://docs.python.org/library/subprocess.html [158]


subprocess.call(['ping', 'localhost'])

其它参考5


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


如果要返回命令的结果,可以使用os.popen。但是,从版本2.6开始不推荐使用这个版本,这有利于子进程模块,其他答案已经很好地解决了。[159] [160]

其它参考6


import os
os.system("your command")


请注意,这是危险的,因为命令没有被清除。我把它留给你去谷歌搜索os和sys模块的相关文档。有一堆函数(exec *和spawn *)这将做类似的事情。

其它参考7


我总是使用fabric这样的事情:


from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )


但这似乎是一个很好的工具:sh(Python子流程接口)。[161]


看一个例子:


from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)

其它参考8


检查pexpectPython库。


它允许交互式控制外部程序/命令,甚至ssh,ftp,telnet等。您只需键入以下内容:


child = pexpect.spawn('ftp 192.168.0.24')

child.expect('(?i)name .*: ')

child.sendline('anonymous')

child.expect('(?i)password')

其它参考9


有许多不同的库允许您使用Python调用外部命令。对于每个库我已经给出了描述并显示了调用外部命令的示例。我用作示例的命令是ls -l(列出所有文件)。如果您想要了解有关任何库的更多信息我已经列出并链接了每个文档的文档。



  的来源:




  • 子流程: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/

  • plumbum:https://plumbum.readthedocs.io/en/latest/

  • pexpect:https://pexpect.readthedocs.io/en/stable/

  • fabric:http://www.fabfile.org/

  • 特使:https://github.com/kennethreitz/envoy

  • 命令:https://docs.python.org/2/library/commands.html




  

    

      这些都是图书馆: [162] [163] [164] [165] [166] [167] [168] [169] [170]

    

  



希望这可以帮助您决定使用哪个库:)



  的



子进程允许您调用外部命令并将它们连接到它们的输入/输出/错误管道(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用于依赖于操作系统的功能。它也可以用于os.systemos.popen调用外部命令(注意:还有一个subprocess.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是一个子进程接口,它允许您像调用函数一样调用程序。如果要多次运行命令,这非常有用。


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的



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



  的特使



特使被称为人类的子过程。它被用作subprocess模块周围的便利包装器。


r = envoy.run("ls -l") # Run command
r.std_out # get output



  的命令



commands包含os.popen的包装函数,但它已从Python 3中删除,因为subprocess是更好的选择。


该编辑基于J.F. Sebastian的评论。

其它参考10


如果您需要来自您正在呼叫的命令的输出,
然后你可以使用subprocess.check_output(Python 2.7 +)。[171]


>>> subprocess.check_output(["ls", "-l", "/dev/null"])
'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\n'


还要注意shell参数。[172]



  如果shell为True,则将通过shell执行指定的命令。如果您主要使用Python来提供它在大多数系统shell上提供的增强控制流,并且仍然希望方便地访问其他shell功能,例如shell管道,文件名通配符,环境变量扩展以及扩展到用户家中,这将非常有用。目录。但请注意,Python本身提供了许多类似shell的功能的实现(特别是globfnmatchos.walk()os.path.expandvars()os.path.expanduser()和[[shutil)。


其它参考11


这就是我运行命令的方式。此代码包含您需要的所有内容


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()

其它参考12


使用标准库



使用子进程模块:[173]


from subprocess import call
call(['ls', '-l'])


这是推荐的标准方式。但是,更复杂的任务(管道,输出,输入等)构造和写入可能很繁琐。


注意:shlex.split可以帮助你解析call和其他subprocess函数的命令,以防万一你不想要(或者你不能!)以列表的形式提供它们: [174]


import shlex
from subprocess import call
call(shlex.split('ls -l'))


具有外部依赖性



如果你不介意外部依赖,请使用plumbum:[175]


from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())


它是最好的subprocess包装器。它是跨平台的,即它适用于Windows和类Unix系统。通过pip install plumbum安装。


另一个受欢迎的图书馆是sh:[176]


from sh import ifconfig
print(ifconfig('wlan0'))


但是,sh删除了Windows支持,所以它不像以前那么棒。安装pip install sh

其它参考13


更新



如果您的代码不需要保持与早期Python版本的兼容性,那么subprocess.run是Python 3.5的推荐方法。它更加一致,并提供与特使相似的易用性。(虽然管道不是直截了当。请参阅这个问题的方法。)[177]


这里有一些来自文档的例子。[179]


运行一个过程:


>>> 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是人类的子进程。[180] [181]


自述文件的用法示例:[182]


>>> 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


没有结果的输出:


import os
os.system("your command here")


输出结果:


import commands
commands.getoutput("your command here")
or
commands.getstatusoutput("your command here")

其它参考15


https://docs.python.org/2/library/subprocess.html[183]


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


import os
os.system('cat testfile')

其它参考16


还有Plumbum [184]


>>> 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

其它参考17


os.system没关系,但有点过时了。它也不是很安全。相反,尝试subprocesssubprocess不直接调用sh,因此比os.system更安全。


在这里获取更多信息。[185]

其它参考18


使用:


import os

cmd = 'ls -al'

os.system(cmd)


os - 此模块提供了一种使用操作系统相关功能的便携方式。


对于更多os函数,这里是文档。[186]

其它参考19



  

在Python中调用外部命令




简单,使用subprocess.run,它返回一个CompletedProcess对象:


>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)


为什么?



从Python 3.5开始,文档推荐subprocess.run:[187]



  调用子进程的推荐方法是对它可以处理的所有用例使用run()函数。对于更高级的用例,可以直接使用底层的Popen接口。



这是一个最简单的使用示例 - 它完全按照要求:


>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> 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传递给适当的stderrstdout:


>>> cp = subprocess.run('python --version', 
                        stderr=subprocess.PIPE, 
                        stdout=subprocess.PIPE)
>>> cp.stderr
b'Python 3.6.1 :: Anaconda 4.4.0 (64-bit)\r\n'
>>> cp.stdout
b''


(我觉得有趣且有点违反直觉,版本信息被放到stderr而不是stdout。)


传递命令列表



人们可以轻松地从手动提供命令字符串(如问题建议)到提供以编程方式构建的字符串。 不要以编程方式构建字符串。这是一个潜在的安全问题。假设您不相信输入,那就更好了。


>>> import textwrap
>>> args = ['python', textwrap.__file__]
>>> cp = subprocess.run(args, stdout=subprocess.PIPE)
>>> cp.stdout
b'Hello there.\r\n  This is indented.\r\n'


注意,只有args应该在位置上传递。


完整签名



这是源中的实际签名,如help(run)所示:



def run(*popenargs, input=None, timeout=None, check=False, **kwargs):



popenargskwargs被赋予Popen构造函数。 input可以是一串字节(或unicode,如果指定编码或universal_newlines=True),它将被传送到子进程的stdin。


文档描述timeout=check=True比我更好:



  timeout参数传递给Popen.communicate()。如果超时
  到期后,子进程将被杀死并等待。该
  在子进程发生后,将重新引发TimeoutExpired异常
  终止。

  
  如果check为true,并且进程以非零退出代码退出,则a
  将引发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代替?我很难根据论据单独找到用例。但是,直接使用Popen可以访问其方法,包括poll,send_signal,terminate和wait。


这里是源中给出的Popen签名。我认为这是信息的最精确封装(与help(Popen)相反):[188]


def __init__(self, args, bufsize=-1, executable=None,
             stdin=None, stdout=None, stderr=None,
             preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
             shell=False, cwd=None, env=None, universal_newlines=False,
             startupinfo=None, creationflags=0,
             restore_signals=True, start_new_session=False,
             pass_fds=(), *, encoding=None, errors=None):


但更多信息是Popen文件:[189]



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=False,
                 startupinfo=None, creationflags=0, restore_signals=True,
                 start_new_session=False, pass_fds=(), *, encoding=None, errors=None)

  
  在新进程中执行子程序。在POSIX上,该类使用
  os.execvp() - 执行子程序的行为。在Windows上,
  该类使用Windows CreateProcess()函数。争论的
  Popen如下。



理解Popen上的其余文档将留给读者练习。

其它参考20


如果你不想测试返回值,subprocess.check_call很方便。它会在任何错误上抛出异常。

其它参考21


这里有另一个不同之处,前面没有提到过。


subprocess.Popen将< command>作为子进程执行。在我的例子中,我需要执行需要与另一个程序进行通信的文件< a>,< b>。


我尝试了subprocess,执行成功了。但是< b>无法与< a>通信。
当我从终端运行时,一切正常。


多一个:
(注意:kwrite与其他应用程序的行为不同。如果您使用Firefox尝试以下操作,结果将不同。)


如果您尝试os.system("kwrite"),程序流将冻结,直到用户关闭kwrite。为了克服这一点,我尝试了os.system(konsole -e kwrite)。这个时间程序继续流动,但kwrite成为控制台的子进程。


任何人都运行kwrite不是一个子进程(即在系统监视器中它必须出现在树的最左边)。

其它参考22


os.system不允许您存储结果,因此如果您想将结果存储在某个列表中,subprocess.call可以正常工作。

其它参考23


我倾向于将子进程与shlex一起使用(以处理引用字符串的转义):[190] [191]


>>> 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)

其它参考24


它可以很简单:


import os
cmd = "your command"
os.system(cmd)

其它参考25


无耻的插件,我为此写了一个库:P
https://github.com/houqp/shell.py[192]


它现在基本上是popen和shlex的包装器。它还支持管道命令,因此您可以在Python中更轻松地链接命令。所以您可以执行以下操作:


ex('echo hello shell.py') | "awk '{print $2}'"

其它参考26


您可以使用Popen,然后您可以检查程序的状态:


from subprocess import Popen

proc = Popen(['ls', '-l'])
if proc.poll() is None:
    proc.kill()


检查subprocess.Popen。[193]

其它参考27


我非常喜欢shell_command,因为它简单。它建立在子流程模块之上。[194]


这是一个来自文档的例子:


>>> 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

其它参考28


从openstack中子获取网络ID:


#!/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)


输出 nova net-list


+--------------------------------------+------------+------+
| 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

其它参考29


在Linux下,如果你想调用一个独立执行的外部命令(将在python脚本终止后继续运行),你可以使用一个简单的队列作为任务假脱机程序或at命令[195] [196]]]


任务假脱机程序的示例:


import os
os.system('ts <your-command>')


关于任务假脱机程序的注释(ts):



  1. 您可以使用以下命令设置要运行的并发进程数(slots):


    ts -S <number-of-slots>

  2. 安装ts并不需要管理员权限。您可以使用简单的make从源代码下载并编译它,将其添加到您的路径中并完成。