添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
酒量大的洋葱  ·  python windows ...·  4 天前    · 
微醺的墨镜  ·  Python ...·  6 天前    · 
性感的饼干  ·  Eto - Convert ...·  1 月前    · 
激动的单车  ·  Blogs/iOS/Swift/Object ...·  2 月前    · 
文质彬彬的手套  ·  Highcharts Class: Chart·  3 月前    · 

一、subprocess

在早期的 Python 版本中,我们主要是通过 os.system()、os.popen()、os.spawn() 等函数来执行命令行指令的,另外还有一个很少使用的 commands 模块。从 Python 2.4 开始, Python 引入 subprocess 模块来管理子进程,以取代一些旧模块的方法。 subprocess 不但可以调用外部的命令作为子进程,而且可以连接到子进程的 input/output/error 管道,获取相关的返回信息。

运行 Python 的时候,我们都是在创建并运行一个进程,像 Linux 进程那样,一个进程可以 fork 一个子进程,并让这个子进程 exec 另外一个程序。在 Python 中,我们通过标准库中的 subprocess 模块来 fork 一个子进程,并运行一个外部的程序。

subprocess 模块中定义有多个创建子进程的函数,这些函数分别以不同的方式创建子进程,所以我们可以根据需要来从中选取一个使用。另外 subprocess 还提供了一些管理标准流(standard stream)和管道(pipe)的工具,从而在进程间使用文本通信。

二、 subprocess. run()

先来看看 subprocess.run() 方法,这也是使用较多的方法。在 Python 3.5 版本中添加,官方文档中提倡通过 subprocess.run() 方法替代其他函数来使用 subproccess 模块的功能。如果需要更高级的用例,可以直接使用基础 Popen 接口。

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None)

运行 args 描述的命令,等待命令完成,然后返回 “CompletedProcess” 实例。

上面显示的参数只是最常见的参数,下面在常用参数中进行了描述。

  • capture_output
  • 如果 capture_output 为 true,则将捕获 stdout 和 stderr。将使用 stdout = PIPE 和 stderr = PIPE 自动创建内部 Popen 对象。stdout 和 stderr 参数也可能无法使用。

  • timeout
  • 很多脚本运行时会卡住,导致调用脚本一直等待,这很显然不是我们想看到的,因此执行命令的超时 Timeout 设置很有必要。好在 subprocess.run() 提供了 timeout 参数,当我们设置 timeout=30 时,表示如果在30秒内无法执行完毕,会抛出异常。

    我们也可以使用 try、except 捕捉这个异常:

    res = subprocess.run('/bin/bash /tmp/for.sh',stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, timeout=30) print('code: ',res.returncode,'stdout: ',res.stdout) except subprocess.TimeoutExpired as e: print(e)
  • input
  • 输入参数传递给 Popen.communicate() ,从而传递给子进程的 stdin。如果使用则必须是 bytes 序列,如果指定了编码或错误或文本为真, 则为字符串。使用时,将使用 stdin = PIPE 自动创建内部 Popen 对象,并且也可能无法使用 stdin 参数。

  • check
  • 如果 check 为 true,并且进程退出时具有非0退出码,则将引发 CalledProcessError 异常。该异常的属性包含参数、退出码及 stdout 和 stderr,如果能捕获到。

  • encoding、error、text
  • 如果指定了 encoding 或 error 或 text=True,则使用指定的 encoding 和 error 或 io.TextIOWrapper 以文本模式打开 stdin、stdout 和 stderr 的文件对象。默认值 universal_newlines 参数等效于文本,是为向后兼容而提供的。默认情况下,文件对象以二进制模式打开。

    >>> subprocess.run(["ls", "-l"]) # doesn't capture output CompletedProcess(args=['ls', '-l'], returncode=0) >>> subprocess.run("exit 1", shell=True, check=True) # CallProcessError Traceback (most recent call last): subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1 >>> subprocess.run(["ls", "-l", "/dev/null"], capture_output=True) # capture output 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', stderr=b'')
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> subprocess .run ( [ "ls" , "-l" ] ) # doesn't capture output
    CompletedProcess ( args = [ 'ls' , '-l' ] , returncode = 0 )
    >>> subprocess .run ( "exit 1" , shell = True , check = True ) # CallProcessError
    Traceback ( most recent call last ) :
    . . .
    subprocess .CalledProcessError : Command 'exit 1' returned non - zero exit status 1
    >>> subprocess .run ( [ "ls" , "-l" , "/dev/null" ] , capture_output = True ) # capture output
    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' , stderr = b '' )

    所以我们可以很轻松可以获取到相关属性信息,比如通过 res.args 属性获取执行的命令和参数,通过 res.returncode 属性获取命令执行结果状态码(当命令执行完成后才可以获取到状态码,其中0表示命令执行成功,1-255都表示命令执行失败),通过 res.stdout 属性获取标准输出信息,通过 res.stderr 属性获取错误输出信息等。这些属性的重要性不言而喻。

    我们可以看到,上面输出的结果是 b’xxxxx’,这是一个 bytes 类型的数据。实际使用中需要将其转换为字符串格式。

    stdout = open('/tmp/subprocess_stdout', 'wb') stderr = open('/tmp/subprocess_stderr', 'wb') popen = subprocess.run(['ping','www.baidu.com'], stdout=stdout.fileno(), stderr=stderr.fileno())
  • shell
  • shell 默认为 False。在 Linux 下,shell=False 时,如果 args 是字符串,那么只能是命令,不能包含任何参数,否则报错;如果 args 是一个列表 list ,则 args 的第一项是定义程序命令字符串,其它项是调用系统 Shell 时的附加参数。

    shell=True 时,如果 args 是字符串,Popen 对象是直接调用系统的 shell 来执行,字符串格式和 shell 终端书写格式一样;如果 args 是一个列表 list,则 args 的第一项是定义程序命令字符串,其它项是调用系统 shell 时的附加参数。官方推荐 shell=True 时,使用字符串方式传递参数。

    如果想使用 shell 中的管道,重定向,文件通配符,环境变量等功能,例如 ”ifconfig | grep eth0 > mm”,那么只能使用 shell=True,并且使用字符串来传递。

    综上, shell=True 功能最强大的,但因为强大也存在安全风险,需要谨慎的对待传递的参数。特别是当参数来自于用户输入时。 这时候可以使用 shlex.quote() 函数来将参数正确的用双引用引起来。

    New in version 3.5.

    Changed in version 3.6: Added encoding and errors parameters

    Changed in version 3.7: Added the text parameter, as a more understandable alias of universal_newlines . Added the capture_output parameter.

    三、subprocess.CompletedProcess

    从 run() 执行完成后返回的对象,表示已完成的进程。

    return CompletedProcess(process.args, retcode, stdout, stderr)

    用于启动进程的参数,这可能是列表或字符串。

    returncode

    子进程的退出状态。通常,退出状态为0表示它已成功运行。负值 -n 表示子进程已被信号 n 终止 (仅限posix)。

    stdout

    从子进程中捕获的 stdout ,bytes 序列,或字符串。如果 run() 调用 encoding、errors or text = True。如果没有捕获 stdout,则为 ”None“。

    如果你使用 stderr=subprocess.STDOUT 运行该进程。stdout、stderr 将在此属性中组合,stderr 将为 “None”。

    stderr

    从子进程中捕获的 stderr,bytes 序列,或字符串。如果 run() 调用 encoding、errors or text = True。如果没有捕获 stderr,则为 ”None“。

    check_returncode()

    如果返回代码为非0,则引发 calledtprocess 异常。

    四、subprocess.Popen

    实际上,上面的几个函数都是基于 Popen 对象的封装(wrapper)。这些封装的目的在于让我们容易使用子进程。当我们想要更个性化我们的需求的时候,就要转向 Popen 类,该类生成的对象用来代表子进程。与上面的封装不同,Popen 对象创建后,主程序不会自动等待子进程完成。我们必须调用对象的 wait() 方法,父进程才会等待(也就是阻塞)。

    while res.poll() is None: out = res.stdout.readline().decode() with open('/tmp/all.log', 'a+') as f: f.write(out)

    其中 poll 方法只有 Popen 对象才有(由于 run 方法是同步的所以不需要 poll 方法),其返回值不为 None 表示命令执行完成了,而返回值不为0表示命令执行失败,poll 方法内部返回的也是 popen.returncode 值,在 Linux 中通过 $? 也是可以获取命令执行成功与否的状态码,0表示执行成功,其他状态码均表示执行失败。

    run() 方法执行命令时必须等待执行完成才可以得到执行结果,而 Popen 对象类似异步执行,父进程无须等待子进程执行完成后才返回结果,父进程执行完就可以去通过 while 获取执行结果。他们的区别决定你使用该如何正确使用。

    另外,run() 方法返回的标准输出是 bytes 类型,直接可以 decode。而 Popen 返回的是 _io.BufferedReader 对象,是一个流,可以使用 read 或者 readlines 方法解析,关于 IO 可以看看官方说明。

    Popen类的实例具有以下方法:

    Popen.poll()

    检查子进程是否已终止。设置并返回 returncode 属性。否则,返回None。

    Popen.wait(timeout=None)

    等待子进程终止。设置并返回 returncode 属性。

    如果进程在超时时间后没有终止,则引发 TimeoutExpired 异常。捕获此异常并重试等待则是安全的。

    Popen.communicate(input=None, timeout=None)

    与进程交互:将数据发送到 stdin,从 stdout 和 stderr 读取数据,直到达到文件结尾。等待进程终止。可选的 input 参数应该是要发送到子进程的数据,如果没有数据发送给子进程,则应该是None。如果在文本模式下打开流,则输入必须是字符串。否则,它必须是字节。

    communicate() 返回一个元组(stdout_data,stderr_data)。如果在文本模式下打开流,则数据将是字符串;否则将是字节。

    另外请注意,如果要将数据发送到进程的 stdin,则需要使用 stdin=PIPE 创建 Popen 对象。同样,要在结果元组中获取除 None 之外的任何内容,你还需要提供 stdout=PIPE 和 stderr=PIPE。

    如果进程在超时后没有终止,则会引发 TimeoutExpired 异常。捕获此异常并重试通信则不会丢失任何输出。

    如果超时到期,则子进程不会被终止,因此为了正确清理,行为良好的应用程序应该主动终止子进程并完成通信:

    发送信号给子进程。

    Popen.terminate()

    停止子进程,在Posix操作系统上,该方法将 SIGTERM 信号发送给子进程。在Windows上,调用Win32 API函数 TerminateProcess() 来停止子进程。

    Popen.kill()

    杀死子进程,在Posix操作系统上,该函数将 SIGKILL 信号发送给子进程。在Windows上,kill() 是 terminate() 的别名。

    Popen.args

    返回被执行的命令和选项,是一个列表或字符串。

    Popen.stdin

    如果 stdin 参数是 PIPE ,则此属性是 open() 方法返回的可写流对象。如果指定了 encoding 或 errors 参数或者 universal_newlines=True,则流是文本流,否则是字节流。如果 stdin 参数不是 PIPE ,则此属性为 None。

    Popen.stdout

    如果 stdout 参数是 PIPE,则此属性是 open() 方法返回的可读流对象。从流中读取子进程提供的标准输出。如果指定了 encoding 或 errors 参数或者 universal_newlines=True ,则流是文本流,否则是字节流。如果 stdout 参数不是 PIPE,则此属性为 None。

    Popen.stderr

    如果 stderr 参数是 PIPE,则此属性是 open() 方法返回的可读流对象。从流中读取子进程提供的错误输出。如果指定了 encoding 或 errors 参数或者 universal_newlines=True,则流是文本流,否则它是字节流。如果 stderr 参数不是 PIPE,则此属性为 None。

    Popen.pid

    子进程的进程ID。请注意,如果将 shell 参数设置为 True,则这是生成的 shell 的进程ID。

    Popen.returncode

    子进程返回码,由 poll() 和 wait() 设置(间接通过communic())。 “None”值表示该进程尚未终止。

    四、subprocess.PIPE

    可以用作 Popen 的 stdin,stdout or stderr 参数的特殊值,表示打开标准流的管道。最适用于 Popen.communicate() 。

    可以在 Popen 对象建立子进程的时候改变标准输入、标准输出和标准错误,并可以利用 subprocess.PIPE 将多个子进程的输入和输出连接在一起,构成管道(pipe),如下例子:

    >>> import subprocess >>> child1 = subprocess.Popen(["cat", "/etc/passwd"], stdout=subprocess.PIPE) >>> child2 = subprocess.Popen(["grep", "0:0"], stdin=child1.stdout, stdout=subprocess.PIPE) >>> print(child2.communicate())

    subprocess.PIPE 实际上为文本流提供一个缓存区。child1 的 stdout 将文本输出到缓存区,随后 child2 的 stdin 从该PIPE中将文本读取走。 child2 的输出文本也被存放在 PIPE 中,直到 communicate() 方法从 PIPE 中读取出 PIPE 中的文本。

    注意:communicate() 方法是 Popen 对象的一个方法,该方法会阻塞父进程,直到子进程完成。

    subprocess 模块对于依赖TTY的外部命令不合适用。 例如,你不能使用它来自动化一个用户输入密码的任务(比如一个ssh会话)。 这时候,你需要使用到第三方模块了,比如基于著名的 expect 家族的工具(pexpect或类似的)。

    https://www.jianshu.com/p/8e582146bd4c

    https://mp.weixin.qq.com/s/2xm_rNb2Vqyb61JKUoHrAg

    http://blog.chinaunix.net/uid-23504396-id-4661783.html

    http://www.10tiao.com/html/160/201708/2649640336/1.html#collapseOne

    https://docs.python.org/3/library/subprocess.html#subprocess.CompletedProcess


    如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
    喜欢 ( 3 )
  • 版权声明

    本站的文章和资源来自互联网或者站长
    的原创,按照 CC BY -NC -SA 3.0 CN
    协议发布和共享,转载或引用本站文章
    应遵循相同协议。如果有侵犯版权的资
    源请尽快联系站长,我们会在24h内删
    除有争议的资源。
  • Linux
  • Database
  • Network
  • Python
  • Ansible中文权威
  • 运维进行时
  • DB-engines
  • Python 3
  • MySQL Blog
  • Redis中文指南
  • Redis命令手册
  • 运维那点事

  •