当前位置:

首页 > 编程开发 > 11 个非常实用的 Python 和 Shell 拿来就用脚本实例!

11 个非常实用的 Python 和 Shell 拿来就用脚本实例!

Python脚本部分实例:企业微信告警、FTP客户端、SSH客户端、Saltstack客户端、vCenter客户端、获取域名ssl证书过期时间、发送今天的天气预报以及未来的天气趋势图;Shell脚本部分实例:SVN完整备份、Zabbix监控用户密码过期、构建本地YUM以及文章中有读者的需求(负载高时,查出占用比较高的进程脚本并存储或推送通知);Python脚本部分企业微信告警此脚本通过企业微信应用,进行微信告警,可用于Zabbix监控。#-*-coding:utf-8

本文目录

    11 个非常实用的 Python 和 Shell 拿来就用脚本实例!

    Python 脚本部分实例:企业微信告警、FTP 客户端、SSH 客户端、Saltstack 客户端、vCenter 客户端、获取域名 ssl 证书过期时间、发送今天的天气预报以及未来的天气趋势图;

    Shell 脚本部分实例:SVN 完整备份、Zabbix 监控用户密码过期、构建本地 YUM 以及文章中有读者的需求(负载高时,查出占用比较高的进程脚本并存储或推送通知);

    Python 脚本部分

    企业微信告警

    此脚本通过企业微信应用,进行微信告警,可用于 Zabbix 监控。

    # -*- coding: utf-8 -*-
    
    
    import requests
    import json
    
    
    class DLF:
    def __init__(self, corpid, corpsecret):
    self.url = "https://qyapi.weixin.qq.com/cgi-bin"
    self.corpid = corpid
    self.corpsecret = corpsecret
    self._token = self._get_token()
    
    def _get_token(self):
    '''
    获取企业微信API接口的access_token
    :return:
    '''
    token_url = self.url + "/gettoken?corpid=%s&corpsecret=%s" %(self.corpid, self.corpsecret)
    try:
    res = requests.get(token_url).json()
    token = res['access_token']
    return token
    except Exception as e:
    return str(e)
    
    def _get_media_id(self, file_obj):
    get_media_url = self.url + "/media/upload?access_token={}&type=file".format(self._token)
    data = {"media": file_obj}
    
    try:
    res = requests.post(url=get_media_url, files=data)
    media_id = res.json()['media_id']
    return media_id
    except Exception as e:
    return str(e)
    
    def send_text(self, agentid, content, touser=None, toparty=None):
    send_msg_url = self.url + "/message/send?access_token=%s" % (self._token)
    send_data = {
    "touser": touser,
    "toparty": toparty,
    "msgtype": "text",
    "agentid": agentid,
    "text": {
    "content": content
    }
    }
    
    try:
    res = requests.post(send_msg_url, data=json.dumps(send_data))
    except Exception as e:
    return str(e)
    
    def send_image(self, agentid, file_obj, touser=None, toparty=None):
    media_id = self._get_media_id(file_obj)
    send_msg_url = self.url + "/message/send?access_token=%s" % (self._token)
    send_data = {
    "touser": touser,
    "toparty": toparty,
    "msgtype": "image",
    "agentid": agentid,
    "image": {
    "media_id": media_id
     }
    }
    
    try:
    res = requests.post(send_msg_url, data=json.dumps(send_data))
    except Exception as e:
    return str(e)

    FTP 客户端

    通过 ftplib 模块操作 ftp 服务器,进行上传下载等操作。

    # -*- coding: utf-8 -*-
    
    from ftplib import FTP
    from os import path
    import copy
    
    
    class FTPClient:
    def __init__(self, host, user, passwd, port=21):
    self.host = host
    self.user = user
    self.passwd = passwd
    self.port = port
    self.res = {'status': True, 'msg': None}
    self._ftp = None
    self._login()
    
    def _login(self):
    '''
    登录FTP服务器
    :return: 连接或登录出现异常时返回错误信息
    '''
    try:
    self._ftp = FTP()
    self._ftp.connect(self.host, self.port, timeout=30)
    self._ftp.login(self.user, self.passwd)
    except Exception as e:
    return e
    
    def upload(self, localpath, remotepath=None):
    '''
    上传ftp文件
    :param localpath: local file path
    :param remotepath: remote file path
    :return:
    '''
    if not localpath: return 'Please select a local file. '
    # 读取本地文件
    # fp = open(localpath, 'rb')
    
    # 如果未传递远程文件路径,则上传到当前目录,文件名称同本地文件
    if not remotepath:
    remotepath = path.basename(localpath)
    
    # 上传文件
    self._ftp.storbinary('STOR ' + remotepath, localpath)
    # fp.close()
    
    def download(self, remotepath, localpath=None):
    '''
    localpath
    :param localpath: local file path
    :param remotepath: remote file path
    :return:
    '''
    
    if not remotepath: return 'Please select a remote file. '
    # 如果未传递本地文件路径,则下载到当前目录,文件名称同远程文件
    if not localpath:
    localpath = path.basename(remotepath)
    # 如果localpath是目录的话就和remotepath的basename拼接
    if path.isdir(localpath):
    localpath = path.join(localpath, path.basename(remotepath))
    
    # 写入本地文件
    fp = open(localpath, 'wb')
    
    # 下载文件
    self._ftp.retrbinary('RETR ' + remotepath, fp.write)
    fp.close()
    
    def nlst(self, dir='/'):
    '''
    查看目录下的内容
    :return: 以列表形式返回目录下的所有内容
    '''
    files_list = self._ftp.nlst(dir)
    return files_list
    
    def rmd(self, dir=None):
    '''
    删除目录
    :param dir: 目录名称
    :return: 执行结果
    '''
    if not dir: return 'Please input dirname'
    res = copy.deepcopy(self.res)
    try:
    del_d = self._ftp.rmd(dir)
    res['msg'] = del_d
    except Exception as e:
    res['status'] = False
    res['msg'] = str(e)
    
    return res
    
    def mkd(self, dir=None):
    '''
    创建目录
    :param dir: 目录名称
    :return: 执行结果
    '''
    if not dir: return 'Please input dirname'
    res = copy.deepcopy(self.res)
    try:
    mkd_d = self._ftp.mkd(dir)
    res['msg'] = mkd_d
    except Exception as e:
    res['status'] = False
    res['msg'] = str(e)
    
    return res
    
    def del_file(self, filename=None):
    '''
    删除文件
    :param filename: 文件名称
    :return: 执行结果
    '''
    if not filename: return 'Please input filename'
    res = copy.deepcopy(self.res)
    try:
    del_f = self._ftp.delete(filename)
    res['msg'] = del_f
    except Exception as e:
    res['status'] = False
    res['msg'] = str(e)
    
    return res
    
    def get_file_size(self, filenames=[]):
    '''
    获取文件大小,单位是字节
    判断文件类型
    :param filename: 文件名称
    :return: 执行结果
    '''
    if not filenames: return {'msg': 'This is an empty directory'}
    res_l = []
    for file in filenames:
    res_d = {}
    # 如果是目录或者文件不存在就会报错
    try:
    size = self._ftp.size(file)
    type = 'f'
    except:
    # 如果是路径的话size显示 - , file末尾加/ (/dir/)
    size = '-'
    type = 'd'
    file = file + '/'
    
    res_d['filename'] = file
    res_d['size'] = size
    res_d['type'] = type
    res_l.append(res_d)
    
    return res_l
    
    def rename(self, old_name=None, new_name=None):
    '''
    重命名
    :param old_name: 旧的文件或者目录名称
    :param new_name: 新的文件或者目录名称
    :return: 执行结果
    '''
    if not old_name or not new_name: return 'Please input old_name and new_name'
    res = copy.deepcopy(self.res)
    try:
    rename_f = self._ftp.rename(old_name, new_name)
    res['msg'] = rename_f
    except Exception as e:
    res['status'] = False
    res['msg'] = str(e)
    
    return res
    
    def close(self):
    '''
    退出ftp连接
    :return:
    '''
    try:
    # 向服务器发送quit命令
    self._ftp.quit()
    except Exception:
    return 'No response from server'
    finally:
    # 客户端单方面关闭连接
    self._ftp.close()

    SSH 客户端

    此脚本仅用于通过 key 连接,如需要密码连接,简单修改下即可。

    # -*- coding: utf-8 -*-
    
    import paramiko
    
    class SSHClient:
    def __init__(self, host, port, user, pkey):
    self.ssh_host = host
    self.ssh_port = port
    self.ssh_user = user
    self.private_key = paramiko.RSAKey.from_private_key_file(pkey)
    self.ssh = None
    self._connect()
    
    def _connect(self):
    self.ssh = paramiko.SSHClient()
    self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
    self.ssh.connect(hostname=self.ssh_host, port=self.ssh_port, username=self.ssh_user, pkey=self.private_key, timeout=10)
    except:
    return 'ssh connect fail'
    
    def execute_command(self, command):
    stdin, stdout, stderr = self.ssh.exec_command(command)
    out = stdout.read()
    err = stderr.read()
    return out, err
    
    def close(self):
    self.ssh.close()

    Saltstack 客户端

    通过 api 对 Saltstack 服务端进行操作,执行命令。

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    
    import requests
    import json
    import copy
    
    
    class SaltApi:
    """
    定义salt api接口的类
    初始化获得token
    """
    def __init__(self):
    self.url = "http://172.85.10.21:8000/"
    self.username = "saltapi"
    self.password = "saltapi"
    self.headers = {"Content-type": "application/json"}
    self.params = {'client': 'local', 'fun': None, 'tgt': None, 'arg': None}
    self.login_url = self.url + "login"
    self.login_params = {'username': self.username, 'password': self.password, 'eauth': 'pam'}
    self.token = self.get_data(self.login_url, self.login_params)['token']
    self.headers['X-Auth-Token'] = self.token
    
    def get_data(self, url, params):
    '''
    请求url获取数据
    :param url: 请求的url地址
    :param params: 传递给url的参数
    :return: 请求的结果
    '''
    send_data = json.dumps(params)
    request = requests.post(url, data=send_data, headers=self.headers)
    response = request.json()
    result = dict(response)
    return result['return'][0]
    
    def get_auth_keys(self):
    '''
    获取所有已经认证的key
    :return:
    '''
    data = copy.deepcopy(self.params)
    data['client'] = 'wheel'
    data['fun'] = 'key.list_all'
    result = self.get_data(self.url, data)
    try:
    return result['data']['return']['minions']
    except Exception as e:
    return str(e)
    
    def get_grains(self, tgt, arg='id'):
    """
    获取系统基础信息
    :tgt: 目标主机
    :return:
    """
    data = copy.deepcopy(self.params)
    if tgt:
    data['tgt'] = tgt
    else:
    data['tgt'] = '*'
    data['fun'] = 'grains.item'
    data['arg'] = arg
    result = self.get_data(self.url, data)
    
    return result
    
    
    def execute_command(self, tgt, fun='cmd.run', arg=None, tgt_type='list', salt_async=False):
    """
    执行saltstack 模块命令,类似于salt '*' cmd.run 'command'
    :param tgt: 目标主机
    :param fun: 模块方法 可为空
    :param arg: 传递参数 可为空
    :return: 执行结果
    """
    data = copy.deepcopy(self.params)
    
    if not tgt: return {'status': False, 'msg': 'target host not exist'}
    if not arg:
    data.pop('arg')
    else:
    data['arg'] = arg
    if tgt != '*':
    data['tgt_type'] = tgt_type
    if salt_async: data['client'] = 'local_async'
    data['fun'] = fun
    data['tgt'] = tgt
    result = self.get_data(self.url, data)
    
    return result
    
    
    def jobs(self, fun='detail', jid=None):
    """
    任务
    :param fun: active, detail
    :param jod: Job ID
    :return: 任务执行结果
    """
    
    data = {'client': 'runner'}
    data['fun'] = fun
    if fun == 'detail':
    if not jid: return {'success': False, 'msg': 'job id is none'}
    data['fun'] = 'jobs.lookup_jid'
    data['jid'] = jid
    else:
    return {'success': False, 'msg': 'fun is active or detail'}
    result = self.get_data(self.url, data)
    
    return result

    vCenter 客户端

    通过官方 SDK 对 vCenter 进行日常操作,此脚本是我用于 cmdb 平台的,自动获取主机信息,存入数据库。

    from pyVim.connect import SmartConnect, Disconnect, SmartConnectNoSSL
    from pyVmomi import vim
    from asset import models
    import atexit
    
    
    class Vmware:
    def __init__(self, ip, user, password, port, idc, vcenter_id):
    self.ip = ip
    self.user = user
    self.password = password
    self.port = port
    self.idc_id = idc
    self.vcenter_id = vcenter_id
    
    def get_obj(self, content, vimtype, name=None):
    '''
    列表返回,name 可以指定匹配的对象
    '''
    container = content.viewManager.CreateContainerView(content.rootFolder, vimtype, True)
    obj = [ view for view in container.view ]
    return obj
    
    def get_esxi_info(self):
    # 宿主机信息
    esxi_host = {}
    res = {"connect_status": True, "msg": None}
    
    try:
    # connect this thing
    si = SmartConnectNoSSL(host=self.ip, user=self.user, pwd=self.password, port=self.port, connectionPoolTimeout=60)
    except Exception as e:
    res['connect_status'] = False
    try:
    res['msg'] = ("%s Caught vmodl fault : " + e.msg) % (self.ip)
    except Exception as e:
    res['msg'] = '%s: connection error' % (self.ip)
    return res
    # disconnect this thing
    atexit.register(Disconnect, si)
    content = si.RetrieveContent()
    esxi_obj = self.get_obj(content, [vim.HostSystem])
    
    for esxi in esxi_obj:
    esxi_host[esxi.name] = {}
    esxi_host[esxi.name]['idc_id'] = self.idc_id
    esxi_host[esxi.name]['vcenter_id'] = self.vcenter_id
    esxi_host[esxi.name]['server_ip'] = esxi.name
    esxi_host[esxi.name]['manufacturer'] = esxi.summary.hardware.vendor
    esxi_host[esxi.name]['server_model'] = esxi.summary.hardware.model
    
    for i in esxi.summary.hardware.otherIdentifyingInfo:
    if isinstance(i, vim.host.SystemIdentificationInfo):
    esxi_host[esxi.name]['server_sn'] = i.identifierValue
    
    # 系统名称
    esxi_host[esxi.name]['system_name'] = esxi.summary.config.product.fullName
    # cpu总核数
    esxi_cpu_total = esxi.summary.hardware.numCpuThreads
    # 内存总量 GB
    esxi_memory_total = esxi.summary.hardware.memorySize / 1024 / 1024 / 1024
    
    # 获取硬盘总量 GB
    esxi_disk_total = 0
    for ds in esxi.datastore:
    esxi_disk_total += ds.summary.capacity / 1024 / 1024 / 1024
    
    # 默认配置4核8G100G,根据这个配置计算剩余可分配虚拟机
    default_configure = {
    'cpu': 4,
    'memory': 8,
    'disk': 100
    }
    
    esxi_host[esxi.name]['vm_host'] = []
    vm_usage_total_cpu = 0
    vm_usage_total_memory = 0
    vm_usage_total_disk = 0
    
    # 虚拟机信息
    for vm in esxi.vm:
    host_info = {}
    host_info['vm_name'] = vm.name
    host_info['power_status'] = vm.runtime.powerState
    host_info['cpu_total_kernel'] = str(vm.config.hardware.numCPU) + '核'
    host_info['memory_total'] = str(vm.config.hardware.memoryMB) + 'MB'
    host_info['system_info'] = vm.config.guestFullName
    
    disk_info = ''
    disk_total = 0
    for d in vm.config.hardware.device:
    if isinstance(d, vim.vm.device.VirtualDisk):
    disk_total += d.capacityInKB / 1024 / 1024
    disk_info += d.deviceInfo.label + ": " +str((d.capacityInKB) / 1024 / 1024) + ' GB' + ','
    
    host_info['disk_info'] = disk_info
    esxi_host[esxi.name]['vm_host'].append(host_info)
    
    # 计算当前宿主机可用容量:总量 - 已分配的
    if host_info['power_status'] == 'poweredOn':
    vm_usage_total_cpu += vm.config.hardware.numCPU
    vm_usage_total_disk += disk_total
    vm_usage_total_memory += (vm.config.hardware.memoryMB / 1024)
    
    esxi_cpu_free = esxi_cpu_total - vm_usage_total_cpu
    esxi_memory_free = esxi_memory_total - vm_usage_total_memory
    esxi_disk_free = esxi_disk_total - vm_usage_total_disk
    
    esxi_host[esxi.name]['cpu_info'] = 'Total: %d核, Free: %d核' % (esxi_cpu_total, esxi_cpu_free)
    esxi_host[esxi.name]['memory_info'] = 'Total: %dGB, Free: %dGB' % (esxi_memory_total, esxi_memory_free)
    esxi_host[esxi.name]['disk_info'] = 'Total: %dGB, Free: %dGB' % (esxi_disk_total, esxi_disk_free)
    
    # 计算cpu 内存 磁盘按照默认资源分配的最小值,即为当前可分配资源
    if esxi_cpu_free < 4 or esxi_memory_free < 8 or esxi_disk_free < 100:
    free_allocation_vm_host = 0
    else:
    free_allocation_vm_host = int(min(
    [
    esxi_cpu_free / default_configure['cpu'],
    esxi_memory_free / default_configure['memory'],
    esxi_disk_free / default_configure['disk']
    ]
    ))
    esxi_host[esxi.name]['free_allocation_vm_host'] = free_allocation_vm_host
    esxi_host['connect_status'] = True
    return esxi_host
    
    def write_to_db(self):
    esxi_host = self.get_esxi_info()
    # 连接失败
    if not esxi_host['connect_status']:
    return esxi_host
    
    del esxi_host['connect_status']
    
    for machine_ip in esxi_host:
    # 物理机信息
    esxi_host_dict = esxi_host[machine_ip]
    # 虚拟机信息
    virtual_host = esxi_host[machine_ip]['vm_host']
    del esxi_host[machine_ip]['vm_host']
    
    obj = models.EsxiHost.objects.create(**esxi_host_dict)
    obj.save()
    
    for host_info in virtual_host:
    host_info['management_host_id'] = obj.id
    obj2 = models.virtualHost.objects.create(**host_info)
    obj2.save()

    获取域名 ssl 证书过期时间

    用于 zabbix 告警

    import re
    import sys
    import time
    import subprocess
    from datetime import datetime
    from io import StringIO
    
    def main(domain):
    f = StringIO()
    comm = f"curl -Ivs https://{domain} --connect-timeout 10"
    
    result = subprocess.getstatusoutput(comm)
    f.write(result[1])
    
    try:
    m = re.search('start date: (.*?)n.*?expire date: (.*?)n.*?common name: (.*?)n.*?issuer: CN=(.*?)n', f.getvalue(), re.S)
    start_date = m.group(1)
    expire_date = m.group(2)
    common_name = m.group(3)
    issuer = m.group(4)
    except Exception as e:
    return 999999999
    
    # time 字符串转时间数组
    start_date = time.strptime(start_date, "%b %d %H:%M:%S %Y GMT")
    start_date_st = time.strftime("%Y-%m-%d %H:%M:%S", start_date)
    # datetime 字符串转时间数组
    expire_date = datetime.strptime(expire_date, "%b %d %H:%M:%S %Y GMT")
    expire_date_st = datetime.strftime(expire_date,"%Y-%m-%d %H:%M:%S")
    
    # 剩余天数
    remaining = (expire_date-datetime.now()).days
    
    return remaining 
    
    if __name__ == "__main__":
    domain = sys.argv[1] 
    remaining_days = main(domain)
    print(remaining_days)

    发送今天的天气预报以及未来的天气趋势图

    11 个非常实用的 Python 和 Shell 拿来就用脚本实例!

    此脚本用于给老婆大人发送今天的天气预报以及未来的天气趋势图,现在微信把网页端禁止了,没法发送到微信了,我是通过企业微信进行通知的,需要把你老婆大人拉到企业微信,无兴趣的小伙伴跳过即可。

    # -*- coding: utf-8 -*-
    
    
    import requests
    import json
    import datetime
    
    def weather(city):
    url = "http://wthrcdn.etouch.cn/weather_mini?city=%s" % city
    
    try:
    data = requests.get(url).json()['data']
    city = data['city']
    ganmao = data['ganmao']
    
    today_weather = data['forecast'][0]
    res = "老婆今天是{}n今天天气概况n城市: {:<10}n时间: {:<10}n高温: {:<10}n低温: {:<10}n风力: {:<10}n风向: {:<10}n天气: {:<10}nn稍后会发送近期温度趋势图,请注意查看。
    ".format(
    ganmao,
    city,
    datetime.datetime.now().strftime('%Y-%m-%d'),
    today_weather['high'].split()[1],
    today_weather['low'].split()[1],
    today_weather['fengli'].split('[')[2].split(']')[0],
    today_weather['fengxiang'],today_weather['type'],
    )
    
    return {"source_data": data, "res": res}
    except Exception as e:
    return str(e)
    ```
    + 获取天气预报趋势图
    ```python
    # -*- coding: utf-8 -*-
    
    
    import matplotlib.pyplot as plt
    import re
    import datetime
    
    
    def Future_weather_states(forecast, save_path, day_num=5):
    '''
    展示未来的天气预报趋势图
    :param forecast: 天气预报预测的数据
    :param day_num: 未来几天
    :return: 趋势图
    '''
    future_forecast = forecast
    dict={}
    
    for i in range(day_num):
    data = []
    date = future_forecast[i]["date"]
    date = int(re.findall("d+",date)[0])
    data.append(int(re.findall("d+", future_forecast[i]["high"])[0]))
    data.append(int(re.findall("d+", future_forecast[i]["low"])[0]))
    data.append(future_forecast[i]["type"])
    dict[date] = data
    
    data_list = sorted(dict.items())
    date=[]
    high_temperature = []
    low_temperature = []
    for each in data_list:
    date.append(each[0])
    high_temperature.append(each[1][0])
    low_temperature.append(each[1][1])
    fig = plt.plot(date,high_temperature,"r",date,low_temperature,"b")
    
    current_date = datetime.datetime.now().strftime('%Y-%m')
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False
    plt.xlabel(current_date)
    plt.ylabel("℃")
    plt.legend(["高温", "低温"])
    plt.xticks(date)
    plt.title("最近几天温度变化趋势")
    plt.savefig(save_path)
    ```
    + 发送到企业微信
    ```python
    # -*- coding: utf-8 -*-
    
    
    import requests
    import json
    
    
    class DLF:
    def __init__(self, corpid, corpsecret):
    self.url = "https://qyapi.weixin.qq.com/cgi-bin"
    self.corpid = corpid
    self.corpsecret = corpsecret
    self._token = self._get_token()
    
    def _get_token(self):
    '''
    获取企业微信API接口的access_token
    :return:
    '''
    token_url = self.url + "/gettoken?corpid=%s&corpsecret=%s" %(self.corpid, self.corpsecret)
    try:
    res = requests.get(token_url).json()
    token = res['access_token']
    return token
    except Exception as e:
    return str(e)
    
    def _get_media_id(self, file_obj):
    get_media_url = self.url + "/media/upload?access_token={}&type=file".format(self._token)
    data = {"media": file_obj}
    
    try:
    res = requests.post(url=get_media_url, files=data)
    media_id = res.json()['media_id']
    return media_id
    except Exception as e:
    return str(e)
    
    def send_text(self, agentid, content, touser=None, toparty=None):
    send_msg_url = self.url + "/message/send?access_token=%s" % (self._token)
    send_data = {
    "touser": touser,
    "toparty": toparty,
    "msgtype": "text",
    "agentid": agentid,
    "text": {
    "content": content
    }
    }
    
    try:
    res = requests.post(send_msg_url, data=json.dumps(send_data))
    except Exception as e:
    return str(e)
    
    def send_image(self, agentid, file_obj, touser=None, toparty=None):
    media_id = self._get_media_id(file_obj)
    send_msg_url = self.url + "/message/send?access_token=%s" % (self._token)
    send_data = {
    "touser": touser,
    "toparty": toparty,
    "msgtype": "image",
    "agentid": agentid,
    "image": {
    "media_id": media_id
     }
    }
    
    try:
    res = requests.post(send_msg_url, data=json.dumps(send_data))
    except Exception as e:
    return str(e)
    + main脚本
    
    # -*- coding: utf-8 -*-
    
    
    from plugins.weather_forecast import weather
    from plugins.trend_chart import Future_weather_states
    from plugins.send_wechat import DLF
    import os
    
    
    # 企业微信相关信息
    corpid = "xxx"
    corpsecret = "xxx"
    agentid = "xxx"
    # 天气预报趋势图保存路径
    _path = os.path.dirname(os.path.abspath(__file__))
    save_path = os.path.join(_path ,'./tmp/weather_forecast.jpg')
    
    # 获取天气预报信息
    content = weather("大兴")
    
    # 发送文字消息
    dlf = DLF(corpid, corpsecret)
    dlf.send_text(agentid=agentid, content=content['res'], toparty='1')
    
    # 生成天气预报趋势图
    Future_weather_states(content['source_data']['forecast'], save_path)
    # 发送图片消息
    file_obj = open(save_path, 'rb')
    dlf.send_image(agentid=agentid, toparty='1', file_obj=file_obj)

    Shell 脚本部分

    SVN 完整备份

    通过 hotcopy 进行 SVN 完整备份,备份保留 7 天。

    #!/bin/bash
    # Filename :svn_backup_repos.sh
    # Date :2020/12/14
    # Author :JakeTian
    # Email:JakeTian@***.com
    # Crontab:59 23 * * * /bin/bash $BASE_PATH/svn_backup_repos.sh >/dev/null 2>&1
    # Notes:将脚本加入crontab中,每天定时执行
    # Description:SVN完全备份
    
    
    set -e
    
    SRC_PATH="/opt/svndata"
    DST_PATH="/data/svnbackup"
    LOG_FILE="$DST_PATH/logs/svn_backup.log"
    SVN_BACKUP_C="/bin/svnadmin hotcopy"
    SVN_LOOK_C="/bin/svnlook youngest"
    TODAY=$(date +'%F')
    cd $SRC_PATH
    ALL_REPOS=$(find ./ -maxdepth 1 -type d ! -name 'httpd' -a ! -name 'bak' | tr -d './')
    
    # 创建备份目录,备份脚本日志目录
    test -d $DST_PATH || mkdir -p $DST_PATH
    test -d $DST_PATH/logs || mkdir $DST_PATH/logs
    test -d $DST_PATH/$TODAY || mkdir $DST_PATH/$TODAY
    
    # 备份repos文件
    for repo in $ALL_REPOS
    do
    $SVN_BACKUP_C $SRC_PATH/$repo $DST_PATH/$TODAY/$repo
    
    # 判断备份是否完成
    if $SVN_LOOK_C $DST_PATH/$TODAY/$repo;then
    echo "$TODAY: $repo Backup Success" >> $LOG_FILE 
    else
    echo "$TODAY: $repo Backup Fail" >> $LOG_FILE
    fi
    done
    
    # # 备份用户密码文件和权限文件
    cp -p authz access.conf $DST_PATH/$TODAY
    
    # 日志文件转储
    mv $LOG_FILE $LOG_FILE-$TODAY
    
    # 删除七天前的备份
    seven_days_ago=$(date -d "7 days ago" +'%F')
    rm -rf $DST_PATH/$seven_days_ago

    zabbix 监控用户密码过期

    用于 Zabbix 监控 Linux 系统用户(shell 为 /bin/bash 和 /bin/sh)密码过期,密码有效期剩余 7 天触发加自动发现用户。

    #!/bin/bash
    
    diskarray=(`awk -F':' '$NF ~ //bin/bash/||//bin/sh/{print $1}' /etc/passwd`)
    length=${#diskarray[@]}
    
    printf "{n"
    printf't'""data":["
    for ((i=0;i<$length;i++))
    do
    printf 'ntt{'
    printf ""{#USER_NAME}":"${diskarray[$i]}"}"
    if [ $i -lt $[$length-1] ];then
    printf ','
    fi
    done
    printf"nt]n"
    printf "}n"
    
    检查用户密码过期
    
    #!/bin/bash
    export LANG=en_US.UTF-8
    
    SEVEN_DAYS_AGO=$(date -d '-7 day' +'%s')
    user="$1"
    
    # 将Sep 09, 2018格式的时间转换成unix时间
    expires_date=$(sudo chage -l $user | awk -F':' '/Password expires/{print $NF}' | sed -n 's/^ //p')
    if [[ "$expires_date" != "never" ]];then
    expires_date=$(date -d "$expires_date" +'%s')
    
    if [ "$expires_date" -le "$SEVEN_DAYS_AGO" ];then
    echo "1"
    else
    echo "0"
    fi
    else
    echo "0"
    fi

    构建本地YUM

    通过 rsync 的方式同步 yum,通过 nginx 只做 http yum 站点;但是 centos6 的镜像最近都不能用了,国内貌似都禁用了,如果找到合适的自行更换地址。

    #!/bin/bash
    # 更新yum镜像
    
    
    RsyncCommand="rsync -rvutH -P --delete --delete-after --delay-updates --bwlimit=1000"
    DIR="/app/yumData"
    LogDir="$DIR/logs"
    Centos6Base="$DIR/Centos6/x86_64/Base"
    Centos7Base="$DIR/Centos7/x86_64/Base"
    Centos6Epel="$DIR/Centos6/x86_64/Epel"
    Centos7Epel="$DIR/Centos7/x86_64/Epel"
    Centos6Salt="$DIR/Centos6/x86_64/Salt"
    Centos7Salt="$DIR/Centos7/x86_64/Salt"
    Centos6Update="$DIR/Centos6/x86_64/Update"
    Centos7Update="$DIR/Centos7/x86_64/Update"
    Centos6Docker="$DIR/Centos6/x86_64/Docker"
    Centos7Docker="$DIR/Centos7/x86_64/Docker"
    Centos6Mysql5_7="$DIR/Centos6/x86_64/Mysql/Mysql5.7"
    Centos7Mysql5_7="$DIR/Centos7/x86_64/Mysql/Mysql5.7"
    Centos6Mysql8_0="$DIR/Centos6/x86_64/Mysql/Mysql8.0"
    Centos7Mysql8_0="$DIR/Centos7/x86_64/Mysql/Mysql8.0"
    MirrorDomain="rsync://rsync.mirrors.ustc.edu.cn"
    
    # 目录不存在就创建
    check_dir(){
    for dir in $*
    do
    test -d $dir || mkdir -p $dir
    done
    }
    
    # 检查rsync同步结果
    check_rsync_status(){
    if [ $? -eq 0 ];then
    echo "rsync success" >> $1
    else
    echo "rsync fail" >> $1
    fi
    }
    
    
    check_dir $DIR $LogDir $Centos6Base $Centos7Base $Centos6Epel $Centos7Epel $Centos6Salt $Centos7Salt $Centos6Update $Centos7Update $Centos6Docker $Centos7Docker $Centos6Mysql5_7 $Centos7Mysql5_7 $Centos6Mysql8_0 $Centos7Mysql8_0
    
    
    # Base yumrepo
    #$RsyncCommand "$MirrorDomain"/repo/centos/6/os/x86_64/ $Centos6Base >> "$LogDir/centos6Base.log" 2>&1
    # check_rsync_status "$LogDir/centos6Base.log"
    $RsyncCommand "$MirrorDomain"/repo/centos/7/os/x86_64/ $Centos7Base >> "$LogDir/centos7Base.log" 2>&1
    check_rsync_status "$LogDir/centos7Base.log"
    
    # Epel yumrepo
    # $RsyncCommand "$MirrorDomain"/repo/epel/6/x86_64/ $Centos6Epel >> "$LogDir/centos6Epel.log" 2>&1
    # check_rsync_status "$LogDir/centos6Epel.log"
    $RsyncCommand "$MirrorDomain"/repo/epel/7/x86_64/ $Centos7Epel >> "$LogDir/centos7Epel.log" 2>&1
    check_rsync_status "$LogDir/centos7Epel.log"
    
    # SaltStack yumrepo
    # $RsyncCommand "$MirrorDomain"/repo/salt/yum/redhat/6/x86_64/ $Centos6Salt >> "$LogDir/centos6Salt.log" 2>&1
    # ln -s $Centos6Salt/archive/$(ls $Centos6Salt/archive | tail -1) $Centos6Salt/latest
    # check_rsync_status "$LogDir/centos6Salt.log"
    $RsyncComman "$MirrorDomain"/repo/salt/yum/redhat/7/x86_64/ $Centos7Salt >> "$LogDir/centos7Salt.log" 2>&1
    check_rsync_status "$LogDir/centos7Salt.log"
    # ln -s $Centos7Salt/archive/$(ls $Centos7Salt/archive | tail -1) $Centos7Salt/latest
    
    # Docker yumrepo
    $RsyncCommand "$MirrorDomain"/repo/docker-ce/linux/centos/7/x86_64/stable/ $Centos7Docker >> "$LogDir/centos7Docker.log" 2>&1
    check_rsync_status "$LogDir/centos7Docker.log"
    
    # centos update yumrepo
    # $RsyncCommand "$MirrorDomain"/repo/centos/6/updates/x86_64/ $Centos6Update >> "$LogDir/centos6Update.log" 2>&1
    # check_rsync_status "$LogDir/centos6Update.log"
    $RsyncCommand "$MirrorDomain"/repo/centos/7/updates/x86_64/ $Centos7Update >> "$LogDir/centos7Update.log" 2>&1
    check_rsync_status "$LogDir/centos7Update.log"
    
    # mysql 5.7 yumrepo
    # $RsyncCommand "$MirrorDomain"/repo/mysql-repo/yum/mysql-5.7-community/el/6/x86_64/ "$Centos6Mysql5_7" >> "$LogDir/centos6Mysql5.7.log" 2>&1
    # check_rsync_status "$LogDir/centos6Mysql5.7.log"
    $RsyncCommand "$MirrorDomain"/repo/mysql-repo/yum/mysql-5.7-community/el/7/x86_64/ "$Centos7Mysql5_7" >> "$LogDir/centos7Mysql5.7.log" 2>&1
    check_rsync_status "$LogDir/centos7Mysql5.7.log"
    
    # mysql 8.0 yumrepo
    # $RsyncCommand "$MirrorDomain"/repo/mysql-repo/yum/mysql-8.0-community/el/6/x86_64/ "$Centos6Mysql8_0" >> "$LogDir/centos6Mysql8.0.log" 2>&1
    # check_rsync_status "$LogDir/centos6Mysql8.0.log"
    $RsyncCommand "$MirrorDomain"/repo/mysql-repo/yum/mysql-8.0-community/el/7/x86_64/ "$Centos7Mysql8_0" >> "$LogDir/centos7Mysql8.0.log" 2>&1
    check_rsync_status "$LogDir/centos7Mysql8.0.log"

    读者需求解答

    负载高时,查出占用比较高的进程脚本并存储或推送通知

    #!/bin/bash
    # 物理cpu个数
    physical_cpu_count=$(egrep 'physical id' /proc/cpuinfo | sort | uniq | wc -l)
    # 单个物理cpu核数
    physical_cpu_cores=$(egrep 'cpu cores' /proc/cpuinfo | uniq | awk '{print $NF}')
    # 总核数
    total_cpu_cores=$((physical_cpu_count*physical_cpu_cores))
    
    # 分别是一分钟、五分钟、十五分钟负载的阈值,其中有一项超过阈值才会触发
    one_min_load_threshold="$total_cpu_cores"
    five_min_load_threshold=$(awk 'BEGIN {print '"$total_cpu_cores"' * "0.8"}')
    fifteen_min_load_threshold=$(awk 'BEGIN {print '"$total_cpu_cores"' * "0.7"}')
    
    # 分别是分钟、五分钟、十五分钟负载平均值
    one_min_load=$(uptime | awk '{print $(NF-2)}' | tr -d ',')
    five_min_load=$(uptime | awk '{print $(NF-1)}' | tr -d ',')
    fifteen_min_load=$(uptime | awk '{print $NF}' | tr -d ',')
    
    # 获取当前cpu 内存 磁盘io信息,并写入日志文件
    # 如果需要发送消息或者调用其他,请自行编写函数即可
    get_info(){
    log_dir="cpu_high_script_log"
    test -d "$log_dir" || mkdir "$log_dir"
    ps -eo user,pid,%cpu,stat,time,command --sort -%cpu | head -10 > "$log_dir"/cpu_top10.log
    ps -eo user,pid,%mem,rss,vsz,stat,time,command --sort -%mem | head -10 > "$log_dir"/mem_top10.log
    iostat -dx 1 10 > "$log_dir"/disk_io_10.log
    }
    
    
    export -f get_info
    
    echo "$one_min_load $one_min_load_threshold $five_min_load $five_min_load_threshold $fifteen_min_load $fifteen_min_load_threshold" | 
    awk '{ if ($1>=$2 || $3>=$4 || $5>=$6) system("get_info") }'

    以上,就是今天分享的全部内容了。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Python
    相关文章 更多
    python删除一个列表里的重复元素的方法与代码示例
    python删除一个列表里的重复元素的方法与代码示例

    学习如何使用Python高效删除列表中的重复元素。本文提供5种常见方法的代码示例,涵盖保持顺序与不保持顺序的场景,适合初学者和进阶开发者参考。

    Python写接口的步骤及完整代码示例
    Python写接口的步骤及完整代码示例

    详细讲解使用Python编写Web接口的核心步骤与完整代码示例,涵盖环境准备、路由定义、数据校验、服务启动及调试测试方法。

    codex怎么读取word文档及docx文件操作教程
    codex怎么读取word文档及docx文件操作教程

    介绍如何利用编程工具读取和解析Word文档(docx格式),涵盖基础文本提取代码示例、结构化数据处理逻辑,以及处理复杂排版时的技术局限与替代方案。

    怎么在Python中调用C语言代码提升性能
    怎么在Python中调用C语言代码提升性能

    详细讲解在Python中调用C语言代码提升性能的完整实践指南,包含原理分析、编译步骤、ctypes与CFFI具体实现、性能对比及常见陷阱。

    Python与C语言的性能差异及底层原理
    Python与C语言的性能差异及底层原理

    深入解析Python与C语言在执行效率上的数量级差异,从编译机制、变量内存布局、动态类型推断到GIL锁,系统阐明底层核心原理。

    Python和C语言哪个更适合编程入门
    Python和C语言哪个更适合编程入门

    客观对比Python与C语言在心智负担、底层机制与学习反馈上的核心差异,提供清晰有据的选择标准与切实的入门学习路径。

    selenium上传文件教程本地文件路径设置方法
    selenium上传文件教程本地文件路径设置方法

    深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

    针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

    langgraph python guide中文入门教程详解
    langgraph python guide中文入门教程详解

    本篇LangGraph Python中文入门教程详解,系统梳理StateGraph状态机定义、节点编写、边与条件路由、检查点持久化(MemorySaver)等核心机制,提供从环境安装到完整循环智能体构建的代码示例与实操指导。

    Windows 安装 Python:从官方入口到虚拟环境的可复现实践
    Windows 安装 Python:从官方入口到虚拟环境的可复现实践

    面向后端开发者,基于 Python 官方文档说明如何在 Windows 上安装 Python、创建 venv 虚拟环境及使用 pip 管理依赖。包含可复制命令、验证方法及工具选择边界,避免常见配置误区。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。