zoukankan      html  css  js  c++  java
  • python中的subprocess.Popen() 执行shell命令

    subprocess介绍

    需要用到Python来执行shell脚本, 因此需要查看下subprocess模块文档。

    根据官网文档描述:subprocess模块用于创建子进程, 这个模块用于替换旧版本中的一些模块, 如:os.system,

    os.spawn*, os.popen*, os.popen*, popen2.*, commands.*, subprocess允许你能创建很多子进程, 创建的时候能能指定子进程和子进程的输入、输出、错误输出管道, 执行后能获取输出结果和执行状态。

    subprocess模块的常用方法用法介绍

    subprocess.run() --> python3.5中新增的函数, 执行指定的命令, 等待命令执行完成后返回一个包含执行结果的 CompletedProcess类的实例。

    subprocess.call(): --> 执行指定的命令, 返回命令执行状态, 功能类似羽os.system(cmd)

    subprocess.check_call(): --> python2.5中新增的函数, 执行指定的命令, 如果执行成功则返回状态码, 否则抛出异常。

    【Tips】: 在python3.5之后的版本中, 官方文档中提倡通过subprocess.run()函数替代其他函数来使用subprocess模块的功能。在python3.5之前的版本中, 我们可以通过subprocess.call()来使用subprocess模块的功能。subprocess.run(), subprocess.call(), subprocess.check_call()都是通过对subprocess.Popen的封装来实现的高级函数。

    ###

    三. 这几个函数的定义以及参数

    subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False, universal_newlines=False)

    subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)

    subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
    参数说明:

    args: 要执行的shell命令, 默认应该是一个字符串序列, 如['ls', '-l'], 也可以是一个字符串如: 'ls -l', 但是此时需要把shell参数的值置为True。

    【Tips】--> shell=True参数会让subprocess.call接受字符串类型的变量作为命令, 并调用shell去执行这个字符串, 当shell=False时, subprocess.call只接受数组变量作为命令, 并将数组的第一个元素作为命令, 剩下的全部作为该命令的参数。官方不推荐使用shell=True。

    p = subprocess.run(["pwd"])
    # p1 = subprocess.run(["cd"])
    # p2 = subprocess.run(["ls", "-l"])
    print(p)

     ###


     ###

    subprocess.Popen

    subprocess的目的就是启动一个新的进程并且与之通信。

    subprocess模块中只定义了一个类: Popen。可以使用Popen来创建进程,并与进程进行复杂的交互。它的构造函数如下:

    class subprocess.Popen( args, 
          bufsize=0, 
          executable=None,
          stdin=None,
          stdout=None, 
          stderr=None, 
          preexec_fn=None, 
          close_fds=False, 
          shell=False, 
          cwd=None, 
          env=None, 
          universal_newlines=False, 
          startupinfo=None, 
          creationflags=0)

    ###

    参数args

    参数args可以是字符串或者序列类型(如:list,元组),用于指定进程的可执行文件及其参数。如果是序列类型,第一个元素通常是可执行文件的路径。我们也可以显式的使用executeable参数来指定可执行文件的路径。

    subprocess.Popen(["cat","test.txt"])
    subprocess.Popen("cat test.txt")
    这两个之中,后者将不会工作。因为如果是一个字符串的话,必须是程序的路径才可以。(考虑unix的api函数exec,接受的是字符串
    列表)
    但是下面的可以工作
    subprocess.Popen("cat test.txt", shell=True)
    这是因为它相当于
    subprocess.Popen(["/bin/sh", "-c", "cat test.txt"])
    在*nix下,当shell=False(默认)时,Popen使用os.execvp()来执行子程序。args一般要是一个【列表】。如果args是个字符串的
    话,会被当做是可执行文件的路径,这样就不能传入任何参数了。

    注意:
    shlex.split()可以被用于序列化复杂的命令参数,比如:

    >>> shlex.split('ls ps top grep pkill')
    ['ls', 'ps', 'top', 'grep', 'pkill']
    >>>import shlex, subprocess
    >>>command_line = raw_input()
    /bin/cat -input test.txt -output "diege.txt" -cmd "echo '$MONEY'" 
    >>>args = shlex.split(command_line)
    >>> print args
    ['/bin/cat', '-input', 'test.txt', '-output', 'diege.txt', '-cmd', "echo '$MONEY'"]
    >>>p=subprocess.Popen(args)

    ###

    可以看到,空格分隔的选项(如-input)和参数(如test.txt)会被分割为列表里独立的项,但引号里的或者转义过的空格不在此列
    。这也有点像大多数shell的行为。

    在*nix下,当shell=True时,如果arg是个字符串,就使用shell来解释执行这个字符串。如果args是个列表,则第一项被视为命令,
    其余的都视为是给shell本身的参数。也就是说,等效于:
    subprocess.Popen(['/bin/sh', '-c', args[0], args[1], ...])

    ####

    参数bufsize

    参数bufsize一般0 无缓冲,1 行缓冲,其他正值 缓冲区大小,负值 采用默认系统缓冲(一般是全缓冲)

    ###

    参数executable

    executable一般不用,args字符串或列表第一项表示程序名

    ###

    参数stdin, stdout, stderr

    参数stdin, stdout, stderr分别表示程序的标准输入、输出、错误句柄。他们可以是PIPE,文件描述符或文件对象,也可以设置为None,表示从父进程继承。

    subprocess.PIPE
      在创建Popen对象时,subprocess.PIPE可以初始化stdin, stdout或stderr参数。表示与子进程通信的标准流。

    subprocess.STDOUT
      创建Popen对象时,用于初始化stderr参数,表示将错误通过标准输出流输出。

    ###

    参数shell

    如果参数shell设为true,程序将通过shell来执行。

    ####

    参数env

    参数env是字典类型,用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。

    ###

    Popen的方法:

    Popen.poll()
      用于检查子进程是否已经结束。设置并返回returncode属性。

    Popen.wait()
      等待子进程结束。设置并返回returncode属性。

    Popen.communicate(input=None)
      与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。可选参数input指定发送到子进程的参数。Communicate()返回一个元组:(stdoutdata, stderrdata)。注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。

    Popen.send_signal(signal)
      向子进程发送信号。

    Popen.terminate()
      停止(stop)子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。

    Popen.kill()
      杀死子进程。

    Popen.pid
      获取子进程的进程ID。

    Popen.returncode
      获取进程的返回值。如果进程还没有结束,返回None。

    ###

    进程通信:

    如果想得到进程的输出,管道是个很方便的方法:

    p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)  
    (stdoutput,erroutput) = p.communicate()  

    ###

    import subprocess
    p = subprocess.Popen("ls", shell=True, stdout=subprocess.PIPE)
    (stdoutput, erroutput) = p.communicate()
    print(stdoutput)
    print(erroutput)

    ####

    p.communicate会一直等到进程退出,并将标准输出和标准错误输出返回,这样就可以得到子进程的输出了。

    上面的例子通过communicate给stdin发送数据,然后使用一个tuple接收命令的执行结果。

    上面,标准输出和标准错误输出是分开的,也可以合并起来,只需要将stderr参数设置为subprocess.STDOUT就可以了,这样子:

    p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)  
    (stdoutput,erroutput) = p.communicate() 

    如果你想一行行处理子进程的输出,也没有问题:

    p=subprocess.Popen("dir", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)  
    while True:  
        buff = p.stdout.readline()  
        if buff == '' and p.poll() != None:  
            break 

    死锁

    但是如果你使用了管道,而又不去处理管道的输出,那么小心点,如果子进程输出数据过多,死锁就会发生了,比如下面的用法:

    p=subprocess.Popen("longprint", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)  
    p.wait()  

    longprint是一个假想的有大量输出的进程,当输出达到4096时,死锁就发生了。当然,如果我们用p.stdout.readline或者p.communicate去清理输出,那么无论输出多少,死锁都是不会发生的。或者我们不使用管道,比如不做重定向,或者重定向到文件,也都是可以避免死锁的。

    subprocess还可以连接起来多个命令来执行。
    在shell中我们知道,想要连接多个命令可以使用管道。
    在subprocess中,可以使用上一个命令执行的输出结果作为下一次执行的输入。例子如下:

     ####
     
     
     
     
     
    #####
  • 相关阅读:
    第十四周学习进度
    二阶段冲刺(七)
    二阶段冲刺(六)
    二阶段冲刺(五)
    二阶段冲刺(四)
    二阶段冲刺(三)
    二阶段冲刺(二)
    二阶段冲刺(一)
    第十三周学习进度
    linux初级学习笔记二:linux操作系统及常用命令,文件的创建与删除和命名规则,命令行展开以及linux中部分目录的作用!(视频序号:02_3)
  • 原文地址:https://www.cnblogs.com/andy0816/p/15624304.html
Copyright © 2011-2022 走看看