使用python执行外部命令subprocess

1、使用python执行外部命令subprocess

subprocess模块是Python自带的模块,无须再另行安装,它主要用来取代一些旧的模块或方法,如os.system、os.spawn*、os.popen*、commands.*等,因此如果需要使用Python调用外部命令或任务时,则优先使用subprocess模块。使用subprocess模块可以方便地执行操作系统支持的命令

下面为基本使用方法

(1)subprocess.run()方法
subprocess.run()是官方推荐使用的方法,几乎所有的工作都可以由它来完成。首先来看一下函数原型:

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, **other_popen_kwargs)

该函数返回一个CompletedProcess类(有属性传入参数及返回值)的实例,虽然该函数的参数有很多,这个了解几个常用的。

args代表需要在操作系统中执行的命令,可以是字符串形式(要求shell=True),也可以是列表list类型。

*代表可变参数,一般是列或字典形式。

stdin、stdout、stderr指定了可执行程序的标准输入、标准输出、标准错误文件句柄。其值可以是 subprocess.PIPE、subprocess.DEVNULL、一个已经存在的文件描述符、已经打开的文件对象或者 None。subprocess.PIPE 表示为子进程创建新的管道。subprocess.DEVNULL 表示使用 os.devnull。默认使用的是 None,表示什么都不做。另外,stderr 可以合并到 stdout 里一起输出。

如果 capture_output 设为 true,stdout 和 stderr 将会被捕获。在使用时,内置的 Popen 对象将自动用 stdout=PIPE 和 stderr=PIPE 创建。stdout 和 stderr 参数不应当与 capture_output 同时提供。如果你希望捕获并将两个流合并在一起,使用 stdout=PIPE 和 stderr=STDOUT 来代替 capture_output。

shell代表着程序是否需要在shell上执行,当想使用shell的特性时,设置shell=True,这样就可以使用shell指令的管道、文件名称通配符、环境变量等,不过Python也提供了许多类shell的模块,如glob、fnmatch、os.walk()、os.path.expandvars()、os.path.expanduser()和shutil。

check如果check设置为True,就检查命令的返回值,当返回值为非0时,将抛出CalledProcessError异常。

timeout设置超时时间,如果超时,则强制kill掉子进程。

encoding: 如果指定了该参数,则 stdin、stdout 和 stderr 可以接收字符串数据,并以该编码方式编码。否则只接收 bytes 类型的数据。

shell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令。

下面举例说明。在Linux系统中如果我们执行一个脚本并获取它的返回值,可有如下两种方法:
方法1:
在这里插入图片描述
方法2:
在这里插入图片描述
如果要捕获脚本的输出,可以按以下做法:

>>> test2=subprocess.run(["ls", "-l", "/root"], stdout=subprocess.PIPE)
>>> test2
CompletedProcess(args=['ls', '-l', '/root'], returncode=0, stdout=b'\xe6\x80\xbb\xe7\x94\xa8\xe9\x87\x8f 28\n-rw-------. 1 root root 1257 9\xe6\x9c\x88  11 2020 anaconda-ks.cfg\n-rw-r--r--. 1 root root  460 3\xe6\x9c\x88  27 08:15 ansible_playbook_\xe7\xa4\xba\xe4\xbe\x8b.yml\n-rw-r--r--. 1 root root  318 3\xe6\x9c\x88  28 08:26 hosts\n-rw-------. 1 root root 1675 3\xe6\x9c\x88  25 13:24 id_rsa\n-rw-r--r--. 1 root root  291 3\xe6\x9c\x88  27 08:21 myplaybook.yml\n-rw-r--r--. 1 root root  174 3\xe6\x9c\x88  28 08:31 script_test.yml\n-rw-r--r--. 1 root root  910 3\xe6\x9c\x88  28 08:19 sys_info.py\n')
>>> test2.stdout
b'\xe6\x80\xbb\xe7\x94\xa8\xe9\x87\x8f 28\n-rw-------. 1 root root 1257 9\xe6\x9c\x88  11 2020 anaconda-ks.cfg\n-rw-r--r--. 1 root root  460 3\xe6\x9c\x88  27 08:15 ansible_playbook_\xe7\xa4\xba\xe4\xbe\x8b.yml\n-rw-r--r--. 1 root root  318 3\xe6\x9c\x88  28 08:26 hosts\n-rw-------. 1 root root 1675 3\xe6\x9c\x88  25 13:24 id_rsa\n-rw-r--r--. 1 root root  291 3\xe6\x9c\x88  27 08:21 myplaybook.yml\n-rw-r--r--. 1 root root  174 3\xe6\x9c\x88  28 08:31 script_test.yml\n-rw-r--r--. 1 root root  910 3\xe6\x9c\x88  28 08:19 sys_info.py\n'
>>> 

如果传入参数check=True,当returncode不为0时,将会抛出subprocess.CalledProcessError异常;如果传输timeout参数,当运行时间超过timeout时就会抛出TimeoutExpired异常。运行结果如下:

test2=subprocess.run("sleep 3",shell=True, timeout=True)
Traceback (most recent call last):
  File "/usr/lib64/python3.6/subprocess.py", line 425, in run
    stdout, stderr = process.communicate(input, timeout=timeout)
  File "/usr/lib64/python3.6/subprocess.py", line 863, in communicate
    stdout, stderr = self._communicate(input, endtime, timeout)
  File "/usr/lib64/python3.6/subprocess.py", line 1560, in _communicate
    self.wait(timeout=self._remaining_time(endtime))
  File "/usr/lib64/python3.6/subprocess.py", line 1469, in wait
    raise TimeoutExpired(self.args, timeout)
subprocess.TimeoutExpired: Command 'sleep 3' timed out after 0.9998351870162878 seconds

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python3.6/subprocess.py", line 430, in run
    stderr=stderr)
subprocess.TimeoutExpired: Command 'sleep 3' timed out after True seconds
>>> test2=subprocess.run("exit 1",shell=True, check=True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python3.6/subprocess.py", line 438, in run
    output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1.

说明超时会抛出TimeoutExpired异常,这在实际工作中非常有用,比如一个任务不确定什么时间完成,可以设置一个超时时间,如果超时仍未完成,可以通过代码控制超时重新运行。如果超时重试3次不成功,就让程序报错退出。
以下是一个使用rsync命令的python脚本:

#! /bin/python3
import subprocess
import datetime


date = datetime.datetime.now()
now = str(date.strftime("%Y-%m-%d"))
print(now)

host = subprocess.run("hostname -I", shell=True, stdout=subprocess.PIPE, encoding="utf-8")
host_ip =  host.stdout.split()[0]
back_server_ip = "192.168.3.90"
backup_dir = r"/backup/"

if subprocess.run(r"[ ! -d {}/{} ]".format(backup_dir, host_ip), shell=True, stdout=subprocess.PIPE).returncode==0:
    subprocess.run(r"mkdir -p {}/{}".format(backup_dir, host_ip), shell=True)

subprocess.run(r"cd /", shell=True)
subprocess.run(r"tar cf {}/{}/sys_file_bak_{}_tar.gz /var/log".format(backup_dir, host_ip, now), shell=True)
subprocess.run(r"tar rf {}/{}/sys_file_bak_{}_tar.gz /etc/rc.d/rc.local".format(backup_dir, host_ip, now), shell=True)
subprocess.run(r"tar rf {}/{}/sys_file_bak_{}_tar.gz /server/scripts".format(backup_dir, host_ip, now), shell=True)
#subprocess.run(r"tar zcf {}/{}/www_{}_tar.gz /etc/rc.d/rc.local".format(backup_dir, host_ip, now), shell=True)
#subprocess.run(r"tar zcf {}/{}/app_logs_{}_tar.gz /app/logs".format(backup_dir, host_ip, now), shell=True)


subprocess.run(r"find {} -type f -name  \"*{}_tar.gz\"  | xargs md5sum > {}/{}/{}.flag".format(backup_dir, now, backup_dir, host_ip, now), shell=True)

subprocess.run(r"rsync -az {} rsync_backup@{}::backup --password-file=/etc/rsync.password".format(backup_dir, back_server_ip), shell=True)

subprocess.run(r"find {} -name *_tar.gz -type f -mtime +7 | xargs rm -f".format(backup_dir), shell=True)

(2)Popen 类
此模块的底层的进程创建与管理由 Popen 类处理。它提供了很大的灵活性,因此开发者能够处理未被便利函数覆盖的不常见用例。Popen 是 subprocess的核心,子进程的创建和管理都靠它处理。

class 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=None, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, group=None, extra_groups=None, user=None, umask=-1, encoding=None, errors=None, text=None)

常用参数:

args:shell命令,可以是字符串或者序列类型(如:list,元组)
bufsize:缓冲区大小。当创建标准流的管道对象时使用,默认-1。
0:不使用缓冲区
1:表示行缓冲,仅当universal_newlines=True时可用,也就是文本模式
正数:表示缓冲区大小
负数:表示使用系统默认的缓冲区大小。
stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
preexec_fn:只在 Unix 平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
shell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令。
cwd:用于设置子进程的当前目录。
env:用于指定子进程的环境变量。如果 env = None,子进程的环境变量将从父进程中继承。

例子:

>>> import subprocess
>>> p = subprocess.Popen('ls -l', shell=True)
>>> total 164
-rw-r--r--  1 root root   133 Jul  4 16:25 admin-openrc.sh
-rw-r--r--  1 root root   268 Jul 10 15:55 admin-openrc-v3.sh
...
>>> p.returncode
>>> p.wait()
0
>>> p.returncode

Popen 对象方法

poll(): 检查进程是否终止,如果终止返回 returncode,否则返回 None。
wait(timeout): 等待子进程终止。
communicate(input,timeout): 和子进程交互,发送和读取数据。
send_signal(singnal): 发送信号到子进程 。
terminate(): 停止子进程,也就是发送SIGTERM信号到子进程。
kill(): 杀死子进程。发送 SIGKILL 信号到子进程。

例子:

import time
import subprocess

def cmd(command):
    subp = subprocess.Popen(command,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE,encoding="utf-8")
    subp.wait(2)
    if subp.poll() == 0:
        print(subp.communicate()[1])
    else:
        print("失败")



cmd("ls /root")
cmd("exit 1")

输出结果如下:

[root@m01 ~]# vim supprocess_test.py
[root@m01 ~]# python3 supprocess_test.py 
1
anaconda-ks.cfg
ansible_hosts
disk.py
pwd.sh
supprocess_test.py
test.txt

失败

参考文章:https://www.runoob.com/w3cnote/python3-subprocess.html

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值