当前位置 博文首页 > Python执行外部命令subprocess的使用详解

    Python执行外部命令subprocess的使用详解

    作者:知无涯学无尽 时间:2021-06-06 18:17

    一、了解subprocess

    • subeprocess模块是python自带的模块,无需安装,主要用来取代一些就的模块或方法,如os.system、os.spawn*、os.popen、commands.*等。
    • 因此执行外部命令优先使用subprocess模块

    1、subprocess.run()方法

    subprocess.run()方法是官方推荐的方法,几乎所有的工作都可以用它来完成。
    如下是函数源码:

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

    该函数返回一个CompletedProcess类(有属性传入参数及返回值)的实例,该函数的参数有很多,只需要记住常用的即可

    1、args : 代表需要在操作系统中执行的命令,可以是字符串形式(要求shell=True),也可以是list列表类型
    2、* :代表可变参数,一般是列表或者字典类型
    3、stdin、stdout、stderr :指定了可执行程序的标准输入、标准输出、标准错误文件句柄
    4、shell :代表着程序是否需要在shell上执行,当想使用shell的特性时,设置shell=True,这样就可以使用shell指令的管道、文件名称通配符、环境变量等,不过python也提供了很多类似shell的模块,如glob、fnmatch、os.walk()、os.path.expandvars()、os.path.expanduser()和shutil。
    5、check :如果check设置为True,就检查命令的返回值,当返回值为0时,将抛出AclledProcessError的异常
    6、timeout :设置超时时间,如果超时则kill掉子进程

    1.使用字符串方式执行shell命令

    [root@localhost python]# vim 1.py
    #!/bin/env python3
    import subprocess
    b=subprocess.run("ls -l /ltp | head -2", shell=True) # 执行run方法,并将返回值赋值给b
    # total 184980
    # -rw-r--r--. 1 root root     10865 May  8 16:21 123.txt
    
    print(b)              # 执行该run函数后返回的CompletedProcess类
    # CompletedProcess(args='ls -l /ltp | head -2', returncode=0)
    
    print(b.args)         # 打印出CompletedProcess类的agrs属性值,就是执行的shell命令
    # ls -l /ltp | head -2
    
    print(b.returncode)   # 打印命令执行的状态码
    # 0

    结果展示

    [root@localhost python]# ./1.py
    total 184980
    -rw-r--r--. 1 root root     10865 May  8 16:21 123.txt

    CompletedProcess(args='ls -l /ltp | head -2', returncode=0)2

    ls -l /ltp | head -2

    0

    2.使用列表方式执行

    个人感觉方法二不好用,尤其是想要使用管道符号是,很难用

    #!/bin/env python3
    import subprocess
    b = subprocess.run(["ls", "-l", "/ltp"])
    print(b)
    print(b.args)
    print(b.returncode)

    执行结果

    [root@localhost python]# ./2.py
    total 10865
    -rw-r--r--. 1 root root     10865 May  8 16:21 123.txt
    CompletedProcess(args=['ls', '-l', '/ltp'], returncode=0)
    ['ls', '-l', '/ltp']
    0

    3.捕获脚本输出

    •  如果需要采集命令执行的结果,可以传入参数stdout=subprocess.PIPE
    [root@localhost python]# cat 3.py
    #!/bin/env python3
    import subprocess
    # 传入stdout=subprocess.PIPE参数即可
    b=subprocess.run("ls -l /ltp | head -2", shell=True, stdout=subprocess.PIPE)
    
    print(b.stdout)

    结果显示

    [root@localhost python]# ./1.py
    b'total 184980\n-rw-r--r--. 1 root root     10865 May  8 16:21 123.txt\n'

    4.检测异常

    示例1:模拟renturncode值不为0

    传入参数check=True,当返回值不为0时,就会抛出异常

    [root@localhost python]# cat 1.py
    #!/bin/env python3
    import subprocess
    b=subprocess.run("ls -l /123 | head -2 && exit 1", shell=True, stdout=subprocess.PIPE, check=True)
    print(b.returncode)

    执行结果:返回了CalledProcessError 类型报错

    [root@localhost python]# ./1.py
    ls: cannot access /123: No such file or directory
    Traceback (most recent call last):
      File "./1.py", line 3, in <module>
        b=subprocess.run("ls -l /123 | head -2 && exit 1", shell=True, stdout=subprocess.PIPE, check=True)
      File "/usr/local/python3/lib/python3.7/subprocess.py", line 487, in run
        output=stdout, stderr=stderr)
    subprocess.CalledProcessError: Command 'ls -l /123 | head -2 && exit 1' returned non-zero exit status 1.
    # 返回了 CalledProcessError 类型报错

    示例2:模拟执行超时

    返回 TimeoutExpired 异常

    [root@localhost python]# vim 1.py
    #!/bin/env python3
    import subprocess
    
    b=subprocess.run("while 2>1;do sleep 1;done",timeout=3, shell=True, stdout=subprocess.PIPE, check=True)
    
    print(b.returncode)

    显示结果

    [root@localhost python]# ./1.py
    Traceback (most recent call last):
      File "/usr/local/python3/lib/python3.7/subprocess.py", line 474, in run
        stdout, stderr = process.communicate(input, timeout=timeout)
      File "/usr/local/python3/lib/python3.7/subprocess.py", line 939, in communicate
        stdout, stderr = self._communicate(input, endtime, timeout)
      File "/usr/local/python3/lib/python3.7/subprocess.py", line 1682, in _communicate
        self._check_timeout(endtime, orig_timeout)
      File "/usr/local/python3/lib/python3.7/subprocess.py", line 982, in _check_timeout
        raise TimeoutExpired(self.args, orig_timeout)
    subprocess.TimeoutExpired: Command 'while 2>1;do sleep 1;done' timed out after 3 seconds

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "./1.py", line 3, in <module>
        b=subprocess.run("while 2>1;do sleep 1;done",timeout=3, shell=True, stdout=subprocess.PIPE, check=True)
      File "/usr/local/python3/lib/python3.7/subprocess.py", line 479, in run
        stderr=stderr)
    subprocess.TimeoutExpired: Command 'while 2>1;do sleep 1;done' timed out after 3 seconds

    2、Popen类

    1.初步认识Popen类

    首先来看一下Popen类的构造函数

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

    参数 字符串或列表bufsize0 : 无缓冲

    参数 字符串或列表
    bufsize 0 : 无缓冲
    1 : 行缓冲
    其他正数值 :缓冲区大小
    负数值 :采用默认的系统缓冲(一般是全缓冲)
    executable 一般不用,args 字符串或列表 的第一项表示程序名
    stdin
    stdout
    stderr
    None : 没有任何重定向 继承父进程
    PIPE : 创建管道
    文件对象
    文件描述符(整数)
    stderr也可以设置为stdout
    preexec_fn 钩子函数,在fork和exec之间执行
    close_fds unix 下执行新进程前是否关闭0/1/2之外的文件
    windows 下不继承还是继承父进程的文件描述
    shell 若为True的话 :
    在unix 下相当于在args前面添加了 “/bin/bash” “-c"
    在windows下,相当于添加了"cmd.exe /c”
    cwd 设置工作目录
    env 设置环境变量
    unviersal_newlines 各种换行符统一处理成"\n"
    startupinfo windows下传递给CreateProcess的结构体
    creationflags windows下,传递CREATE_NEW_CONSOLE创建自己的控制台窗口

    2.Popen的使用方法

    1、subprocess.Popen([“cat”, “abc.txt”])
    2、subprocess.Popen(“cat abc.txt”, shell=True)

    上面的第二种其实就相当于:subprocess.Popen(["/bin/bash", “-c”, “cat abc.txt”])

    示例:

    [root@localhost python]# cat 3.py
    #!/bin/env python3
    import subprocess
    obj = subprocess.Popen("ls -l /ltp",
                           shell=True,
                           stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE,
                           stderr=subprocess.PIPE,
                           universal_newlines=True)
    error_info = obj.stderr.read()
    out_info = obj.stdout.read()
    result = out_info + error_info
    print(result)
    
    [root@localhost python]# ./3.py
    total 184980
    -rw-r--r--. 1 root root     10865 May  8 16:21 123.txt
    -rw-r--r--. 1 root root       802 Apr 21 09:42 ab.sh
    drwxr-xr-x. 3 root root       101 Apr  1 18:34 auth
    -rw-r--r--. 1 root root   5242880 Mar 18 13:20 bigfile
    -rwxrwxrwx. 1 root root      1392 Feb  5 09:24 dingding.sh

    Popen类的对象方法

    名称 功能
    poll() 检查是否结束,设置返回值
    wait() 等待结束,设置返回值
    communicate() 参数是标准输入,返回标准输出和标准出错
    send_signal() 发动信号(主要指linux下有用)
    terminate() 终止进程,unix对应的SIGTERM信号,windows下调用api函数TerminateProcess()
    kill() 杀死进程(unix对应SIGKILL信号),windows下同上
    stdin
    stdout
    stderr
    参数中指定PIPE时有用
    pid 进程id
    returncode 进程返回值

    补充:其他方法

    1、subeprocess.call(*args,**kwargs): call()方法调用Popen()执行程序,并且等待它执行完成
    2、subpeocess.check_call(*args, **kwargs): 调用上面的call(),如果返回值非零,返回异常
    3、subprocess.check_output(*args, **kwargs) : 调用Popen()执行程序,并返回标准输出

    二、补充os模块执行外部命令

    1、os.system()方法

    示例:

    [root@localhost python]# cat 4.py
    #!/bin/env python3
    import os
    # 变量ret接收命令执行后的返回值
    ret = os.system('ls -l /ltp |head -2')
    print("\n执行成功" if ret == 0 else "\n执行失败")

    执行结果

    [root@localhost python]# ./4.py
    total 184980
    -rw-r--r--. 1 root root     10865 May  8 16:21 123.txt

    执行成功

    2、os.popen()用法

    与subprocess.Popen()类似,就不写了

    补充:subprocess.run()和subprocess.Popen()的执行结果是写入到缓存的,可以执行结束后打印结果,不会实时在终端输出;而os.system()是实时输出到终端界面的;

    js
    下一篇:没有了