茅台最新任务脚本

茅台最新任务脚本

–小白教程—

这个脚本的作用是实现i茅台应用的自动预约功能,主要功能包括生成请求头、预约商品、计算距离和库存情况、发送微信推送消息等。
在这里插入图片描述

代码如下#!/usr/bin/python3
'''
cron: 0 0 9/21 * * *
new Env('i茅台')
'''import logging
import sysimport datetime
import json
import math
import random
import re
import time
import requests
import hashlib
import logging
import pytz
from Crypto.Cipher import AES
import base64
import osconfigs = os.environ["Imaotai"]
configs=a=eval(configs)
#格式如下:抓包取得token,lat和lng代表经纬度,可在https://lbs.amap.com/tools/picker获得
#  [{
#     'phone': '159********',
#     'province': '**省',
#     'city': '**市',
#     'token': '***',
#     'userid': '11672*****',
#     'lat': '***.56797',
#     'lng': '***.90431'
# }]class Encrypt:
def __init__(self, key, iv):
self.key = key.encode('utf-8')
self.iv = iv.encode('utf-8')# @staticmethod
def pkcs7padding(self, text):
"""明文使用PKCS7填充 """
bs = 16
length = len(text)
bytes_length = len(text.encode('utf-8'))
padding_size = length if (bytes_length == length) else bytes_length
padding = bs - padding_size % bs
padding_text = chr(padding) * padding
self.coding = chr(padding)
return text + padding_textdef aes_encrypt(self, content):
""" AES加密 """
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
# 处理明文
content_padding = self.pkcs7padding(content)
# 加密
encrypt_bytes = cipher.encrypt(content_padding.encode('utf-8'))
# 重新编码
result = str(base64.b64encode(encrypt_bytes), encoding='utf-8')
return resultdef aes_decrypt(self, content):
"""AES解密 """
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
content = base64.b64decode(content)
text = cipher.decrypt(content).decode('utf-8')
return text.rstrip(self.coding)# process.py
AES_KEY = 'qbhajinldepmucsonaaaccgypwuvcjaa'
AES_IV = '2018534749963515'
SALT = '2af72f100c356273d46284f6fd1dfc08'current_time = str(int(time.time() * 1000))
headers = {}
mt_version = "".join(re.findall('latest__version">(.*?)</p>',
requests.get(
'https://apps.apple.com/cn/app/i%E8%8C%85%E5%8F%B0/id1600482450').text,
re.S)).split(" ")[1]header_context = f'''
MT-Lat: 28.499562
MT-K: 1675213490331
MT-Lng: 102.182324
Host: app.moutai519.com.cn
MT-User-Tag: 0
Accept: */*
MT-Network-Type: WIFI
MT-Token: 1
MT-Team-ID:
MT-Info: 028e7f96f6369cafe1d105579c5b9377
MT-Device-ID: 2F2075D0-B66C-4287-A903-DBFF6358342A
MT-Bundle-ID: com.moutai.mall
Accept-Language: en-CN;q=1, zh-Hans-CN;q=0.9
MT-Request-ID: 167560018873318465
MT-APP-Version: 1.3.7
User-Agent: iOS;16.3;Apple;?unrecognized?
MT-R: clips_OlU6TmFRag5rCXwbNAQ/Tz1SKlN8THcecBp/HGhHdw==
Content-Length: 93
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: application/json
userId: 2
'''def init_headers(user_id: str = '1', token: str = '2', lat: str = '28.499562', lng: str = '102.182324'):
for k in header_context.rstrip().lstrip().split("\n"):
temp_l = k.split(': ')
dict.update(headers, {temp_l[0]: temp_l[1]})
dict.update(headers, {"userId": user_id})
dict.update(headers, {"MT-Token": token})
dict.update(headers, {"MT-Lat": lat})
dict.update(headers, {"MT-Lng": lng})
dict.update(headers, {"MT-APP-Version": mt_version})def signature(data: dict):
keys = sorted(data.keys())
temp_v = ''
for item in keys:
temp_v += data[item]
text = SALT + temp_v + current_time
hl = hashlib.md5()
hl.update(text.encode(encoding='utf8'))
md5 = hl.hexdigest()
return md5def get_vcode(mobile: str):
params = {'mobile': mobile}
md5 = signature(params)
dict.update(
params, {'md5': md5, "timestamp": current_time, 'MT-APP-Version': mt_version})
responses = requests.post("https://app.moutai519.com.cn/xhr/front/user/register/vcode", json=params,
headers=headers)logging.info(
f'get v_code : params : {params}, response code : {responses.status_code}, response body : {responses.text}')def login(mobile: str, v_code: str):
params = {'mobile': mobile, 'vCode': v_code, 'ydToken': '', 'ydLogId': ''}
md5 = signature(params)
dict.update(
params, {'md5': md5, "timestamp": current_time, 'MT-APP-Version': mt_version})
responses = requests.post("https://app.moutai519.com.cn/xhr/front/user/register/login", json=params,
headers=headers)
if responses.status_code != 200:
logging.info(
f'login : params : {params}, response code : {responses.status_code}, response body : {responses.text}')
dict.update(headers, {'MT-Token': responses.json()['data']['token']})
dict.update(headers, {'userId': responses.json()['data']['userId']})
return responses.json()['data']['token'], responses.json()['data']['userId']def get_current_session_id():
day_time = get_day_time()
responses = requests.get(
f"https://static.moutai519.com.cn/mt-backend/xhr/front/mall/index/session/get/{day_time}")
if responses.status_code != 200:
logging.warning(
f'get_current_session_id : params : {day_time}, response code : {responses.status_code}, response body : {responses.text}')
current_session_id = responses.json()['data']['sessionId']
dict.update(headers, {'current_session_id': str(current_session_id)})def get_day_time():# 创建一个东八区(北京时间)的时区对象
beijing_tz = pytz.timezone('Asia/Shanghai')# 获取当前北京时间的日期和时间对象
beijing_dt = datetime.datetime.now(beijing_tz)# 设置时间为0点
beijing_dt = beijing_dt.replace(hour=0, minute=0, second=0, microsecond=0)# 获取时间戳(以秒为单位)
timestamp = int(beijing_dt.timestamp()) * 1000
return timestampdef get_location_count(province: str,
city: str,
item_code: str,
p_c_map: dict,
source_data: dict,
lat: str = '28.499562',
lng: str = '102.182324'):
day_time = get_day_time()
session_id = headers['current_session_id']
responses = requests.get(
f"https://static.moutai519.com.cn/mt-backend/xhr/front/mall/shop/list/slim/v3/{session_id}/{province}/{item_code}/{day_time}")
if responses.status_code != 200:
logging.warning(
f'get_location_count : params : {day_time}, response code : {responses.status_code}, response body : {responses.text}')
shops = responses.json()['data']['shops']if MAX_ENABLED:
return max_shop(city, item_code, p_c_map, province, shops)
if DISTANCE_ENABLED:
return distance_shop(city, item_code, p_c_map, province, shops, source_data, lat, lng)def distance_shop(city,
item_code,
p_c_map,
province,
shops,
source_data,
lat: str = '28.499562',
lng: str = '102.182324'):
# shop_ids = p_c_map[province][city]
temp_list = []
for shop in shops:
shopId = shop['shopId']
items = shop['items']
item_ids = [i['itemId'] for i in items]
# if shopId not in shop_ids:
#     continue
if str(item_code) not in item_ids:
continue
shop_info = source_data.get(shopId)
# d = geodesic((lat, lng), (shop_info['lat'], shop_info['lng'])).km
d = math.sqrt(
(float(lat) - shop_info['lat']) ** 2 + (float(lng) - shop_info['lng']) ** 2)
# print(f"距离:{d}")
temp_list.append((d, shopId))# sorted(a,key=lambda x:x[0])
temp_list = sorted(temp_list, key=lambda x: x[0])
# logging.info(f"所有门店距离:{temp_list}")
if len(temp_list) > 0:
return temp_list[0][1]
else:
return '0'def max_shop(city, item_code, p_c_map, province, shops):
max_count = 0
max_shop_id = '0'
shop_ids = p_c_map[province][city]
for shop in shops:
shopId = shop['shopId']
items = shop['items']if shopId not in shop_ids:
continue
for item in items:
if item['itemId'] != str(item_code):
continue
if item['inventory'] > max_count:
max_count = item['inventory']
max_shop_id = shopId
logging.debug(
f'item code {item_code}, max shop id : {max_shop_id}, max count : {max_count}')
return max_shop_idencrypt = Encrypt(key=AES_KEY, iv=AES_IV)def act_params(shop_id: str, item_id: str):session_id = headers['current_session_id']
userId = headers['userId']
params = {"itemInfoList": [{"count": 1, "itemId": item_id}],
"sessionId": int(session_id),
"userId": userId,
"shopId": shop_id
}
s = json.dumps(params)
act = encrypt.aes_encrypt(s)
params.update({"actParam": act})
return paramsdef send_email(msg: str):
if PUSH_TOKEN is None:
return
title = 'imoutai预约失败'  # 改成你要的标题内容
content = msg  # 改成你要的正文内容
url = 'http://www.pushplus.plus/send'
r = requests.get(url, params={'token': PUSH_TOKEN,
'title': title,
'content': content})
logging.info(f'通知推送结果:{r.status_code, r.text}')def reservation(params: dict, mobile: str):
params.pop('userId')
responses = requests.post("https://app.moutai519.com.cn/xhr/front/mall/reservation/add", json=params,
headers=headers)
if responses.status_code == 401:
send_email(f'[{mobile}],登录token失效,需要重新登录')
raise RuntimeError
if '您的实名信息未完善或未通过认证' in responses.text:
send_email(f'[{mobile}],{responses.text}')
raise RuntimeError
logging.info(
f'预约 : mobile:{mobile} :  response code : {responses.status_code}, response body : {responses.text}')def select_geo(i: str):
# https://www.piliang.tech/geocoding-amap
url = f"https://www.piliang.tech/api/amap/geocode?address={i}"
resp = requests.get(url)
print(url)
geocodes: list = resp.json()['geocodes']
return geocodesdef get_map(lat: str = '28.499562', lng: str = '102.182324'):
p_c_map = {}
url = 'https://static.moutai519.com.cn/mt-backend/xhr/front/mall/resource/get'
headers = {
'X-Requested-With': 'XMLHttpRequest',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0_1 like Mac OS X)',
'Referer': 'https://h5.moutai519.com.cn/gux/game/main?appConfig=2_1_2',
'Client-User-Agent': 'iOS;16.0.1;Apple;iPhone 14 ProMax',
'MT-R': 'clips_OlU6TmFRag5rCXwbNAQ/Tz1SKlN8THcecBp/HGhHdw==',
'Origin': 'https://h5.moutai519.com.cn',
'MT-APP-Version': mt_version
}{random.randint(1111111, 999999999)}{int(time.time() * 1000)}',
'Accept-Language': 'zh-CN,zh-Hans;q=1',
'MT-Device-ID': f'{int(time.time() * 1000)}{random.randint(1111111, 999999999)}{int(time.time() * 1000)}',
'Accept': 'application/json, text/javascript, */*; q=0.01',
'mt-lng': f'{lng}',
'mt-lat': f'{lat}'
}
res = requests.get(url, headers=headers, )
mtshops = res.json().get('data', {}).get('mtshops_pc', {})
urls = mtshops.get('url')
r = requests.get(urls)
for k, v in dict(r.json()).items():
provinceName = v.get('provinceName')
cityName = v.get('cityName')
if not p_c_map.get(provinceName):
p_c_map[provinceName] = {}
if not p_c_map[provinceName].get(cityName, None):
p_c_map[provinceName][cityName] = [k]
else:
p_c_map[provinceName][cityName].append(k)return p_c_map, dict(r.json())def getUserEnergyAward(mobile: str):
"""
领取耐力
"""
cookies = {
'MT-Device-ID-Wap': headers['MT-Device-ID'],
'MT-Token-Wap': headers['MT-Token'],
'YX_SUPPORT_WEBP': '1',
}
response = requests.post('https://h5.moutai519.com.cn/game/isolationPage/getUserEnergyAward', cookies=cookies,
headers=headers, json={})
# response.json().get('message') if '无法领取奖励' in response.text else "领取奖励成功"
logging.info(
f'领取耐力 : mobile:{mobile} :  response code : {response.status_code}, response body : {response.text}')########################
# config
ITEM_MAP = {
"10941": "53%vol 500ml贵州茅台酒(甲辰龙年)",
"10942": "53%vol 375ml×2贵州茅台酒(甲辰龙年)",
"10056": "53%vol 500ml茅台1935",
"2478": "53%vol 500ml贵州茅台酒(珍品)"
}# 需要预约的商品(默认只预约2个兔茅)
########################
ITEM_CODES = ['10941', '10942']# push plus 微信推送,具体使用参考  https://www.pushplus.plus
# 例如:PUSH_TOKEN = '123456'
########################
# 不填不推送消息,一对一发送
PUSH_TOKEN = '9265ac3f9ab34138a56f68a1c4624e93'
######################### credentials 路径,例如:CREDENTIALS_PATH = /home/user/.imoutai/credentials
# 不配置,使用默认路径,在宿主目录
# 例如:CREDENTIALS_PATH = '/home/user/.imautai/credentials'
########################
CREDENTIALS_PATH = None
######################### 预约规则配置
########################
# 预约本市出货量最大的门店
MAX_ENABLED = True
# 预约你的位置附近门店
DISTANCE_ENABLED = False
########################DATE_FORMAT = "%m/%d/%Y %H:%M:%S %p"
logging.basicConfig(level=logging.INFO,
# 定义输出log的格式
format='%(asctime)s  %(filename)s : %(levelname)s  %(message)s',
stream=sys.stdout,
datefmt=DATE_FORMAT)# 获取当日session id
get_current_session_id()if len(configs) == 0:
logging.error("配置文件未找到配置")
sys.exit(1)for config in configs:
mobile = config["phone"]
province = config['province']
city = config['city']
token = config['token']
userId = config['userid']
lat = config['lat']
lng = config['lng']p_c_map, source_data = get_map(lat=lat, lng=lng)init_headers(user_id=userId, token=token, lng=lng, lat=lat)
# 根据配置中,要预约的商品ID,城市 进行自动预约
try:
for item in ITEM_CODES:
max_shop_id = get_location_count(province=province,
city=city,
item_code=item,
p_c_map=p_c_map,
source_data=source_data,
lat=lat,
lng=lng)
print(f'max shop id : {max_shop_id}')
if max_shop_id == '0':
continue
shop_info = source_data.get(str(max_shop_id))
title = ITEM_MAP.get(item)
logging.info(f'商品:{title}, 门店:{shop_info["name"]}')
reservation_params = act_params(max_shop_id, item)
reservation(reservation_params, mobile)
getUserEnergyAward(mobile)
except BaseException as e:
print(e)
logging.error(e)

本结构

  • 定时任务:通过 cron 表达式 0 0 9/21 * * *,表示每月的9日和21日的0点执行。

  • 配置说明:从环境变量 Imaotai 获取用户配置,包括手机号、城市、token等信息。

主要功能

  1. AES加密类 Encrypt
  • 用于请求参数的加密,采用AES加密方式。

  • aes_encrypt 方法:对请求内容进行AES加密,确保数据在传输过程中不被篡改。

  • pkcs7padding 方法:实现PKCS7填充,以便加密数据块对齐。

  1. 初始化请求头 init_headers
  • 从固定的 header_context 生成HTTP请求头,并加入用户的userIdMT-Token等动态信息,以模仿真实的请求。
  1. 签名生成 signature
  • 用于生成请求参数的签名,确保请求的唯一性和合法性。签名由 SALT + sorted_params + current_time 生成MD5值。
  1. 获取验证码 get_vcode** 和 **登录 login
  • get_vcode:获取登录验证码,使用手机号作为参数。

  • login:使用验证码进行登录,并返回 tokenuserId

  1. 获取当前会话ID get_current_session_id
  • 用于获取每天的 sessionId,这是预约商品的必要参数之一。
  1. 获取当天0点时间戳 get_day_time
  • 获取北京时间当天0点的时间戳,作为预约请求的时间参数。
  1. 查找预约商品的门店 get_location_count
  • 获取符合条件的门店(如本市最大库存或距离最近的门店),提供给预约请求。
  1. 预约商品 reservation
  • 发送预约请求,提交商品和门店的参数,如 sessionIdshopIditemId 等。

  • 如果token失效或认证失败,发送通知邮件提醒。

  1. 推送通知 send_email
  • 使用Push Plus推送通知,将预约失败的消息通过微信推送给用户。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/58772.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

​CSS之三

CSS三大特性 CSS 有三个非常重要的三个特性:层圣性、继承性、优先级 层叠性 相同选择器给设置相同的样式&#xff0c;此时一个样式就会覆盖(层曼)另一个冲突的样式。层曼性主要解决样式冲突的问题 层叠性原则: - 样式冲突&#xff0c;遵循的原则是就近原则&#xff0c;哪个…

C++设计模式创建型模式———简单工厂模式、工厂方法模式、抽象工厂模式

文章目录 一、引言二、简单工厂模式三、工厂方法模式三、抽象工厂模式四、总结 一、引言 创建一个类对象的传统方式是使用关键字new &#xff0c; 因为用 new 创建的类对象是一个堆对象&#xff0c;可以实现多态。工厂模式通过把创建对象的代码包装起来&#xff0c;实现创建对…

python爬虫抓取豆瓣数据教程

环境准备 在开始之前&#xff0c;你需要确保你的Python环境已经安装了以下库&#xff1a; requests&#xff1a;用于发送HTTP请求。BeautifulSoup&#xff1a;用于解析HTML文档。 如果你还没有安装这些库&#xff0c;可以通过以下命令安装&#xff1a; pip install requests…

代码-画图函数示例

热力图 import matplotlib.pyplot as plt import seaborn as sns import numpy as npdef create_heatmap(people, categories, dataNone, title热力图, xlabel类别, ylabel人员,value_range(0.6, 0.95), figsize(10, 6),cmapYlOrRd, decimal_places3):"""创建热…

2024最新Twitter养号全面指南,品牌起号必看!

X (Twitter)作为活跃用户数以亿计的社交媒体平台&#xff0c;用户数依然在不断增长&#xff0c;其中巨大的流量吸引着个人用户与品牌和卖家。 Twitter养号是有必要的&#xff0c;有大量案例表明养好号&#xff0c;可以大幅度降低账号被冻结的几率&#xff0c;并提升账号的稳定…

百度如何打造AI原生研发新范式?

&#x1f449;点击即可下载《百度AI原生研发新范式实践》资料 2024年10月23-25日&#xff0c;2024 NJSD技术盛典暨第十届NJSD软件开发者大会、第八届IAS互联网架构大会在南京召开。本届大会邀请了工业界和学术界的专家&#xff0c;优秀的工程师和产品经理&#xff0c;以及其它行…

基于大语言模型(LLM)自主Agent 智能体综述

近年来,LLM(Large Language Model)取得了显著成功,并显示出了达到人类智能的巨大潜力。基于这种能力,使用LLM作为中央控制器来构建自助Agent,以获得类人决策能力。 Autonomous agents 又被称为智能体、Agent。指能够通过感知周围环境、进行规划以及执行动作来完成既定任务。…

电脑怎么设置开机密码:保障个人信息安全的第一步

在数字化时代&#xff0c;个人信息的安全至关重要。电脑作为我们日常工作和生活中不可或缺的设备&#xff0c;存储了大量的私人数据和敏感信息。为了防止未经授权的访问&#xff0c;设置开机密码是保护个人隐私和信息安全的基本措施之一。本文将详细介绍如何在不同操作系统下为…

分析 std::optional 的使用与常见错误

文章目录 引言常见错误及解决方案1. 错误使用 std::optional 变量进行算术运算2. 错误检查 std::optional 是否有值3. 忽视 std::optional 的默认值 结论 引言 std::optional 是 C17 引入的一个模板类&#xff0c;用于表示可能有也可能没有值的情况。它特别适用于函数返回值&a…

DB-GPT系列(二):DB-GPT部署(镜像一键部署、源码部署)

一、简介 DB-GPT 是一个开源项目&#xff0c;其将大语言模型 LLM 与数据库紧密结合。该项目主要致力于探索如何让预训练的大规模语言模型&#xff08;例如 GPT&#xff09;能够直接与数据库进行交互&#xff0c;从而生成更为准确且信息丰富的回答。 DB-GPT部署后能否直接使用…

Web组件之 Listener (监听器)

文章目录 1.1 Listener概述1.2 Listener快速入门① xml版本② 注解版本 1.3 案例&#xff1a;模拟spring框架 1.1 Listener概述 ​ JavaWeb 中的监听器是监听 ServletContext HttpSession HttpServletRequest 三个数据域对象创建和销毁以及监听数据域对象中数据的变化&#xf…

【论文翻译】IJCAI 2019 | Graph WaveNet:用于深度时空图建模的Graph WaveNet

论文题目Graph WaveNet for Deep Spatial-Temporal Graph Modeling作者团队Zonghan Wu, Shirui Pan, Guodong Long, Jing Jiang, Chengqi Zhang机构澳大利亚悉尼科技大学人工智能中心 (UTS) 和 澳大利亚莫纳什大学发表会议IJCAI 2019论文链接https://www.ijcai.org/proceedings…

Java数组的定义与使用

今天来学习Java数组的定义与使用 目录 1 数组的基本概念1.1 数组的意义1.2 数组的定义1.3 数组的创建及初始化1.3.1 数组的创建1.3.2 数组的初始化 1.4 数组的使用1.4.1 数组中的元素访问1.4.2 遍历数组运行结果运行结果 2 数组是引用类型2.1 初始 JVM 的内存分布2.2 基本类型变…

https://tieba.baidu.com/p/9247698007

微深节能的库区智能化无人天车管理系统结合了格雷母线技术&#xff0c;提供了一种高精度的定位解决方案。格雷母线系统能够实现连续或断续的位置检测&#xff0c;精度高达≤5mm&#xff0c;适用于需要高精度作业的场景&#xff0c;如货物搬运和堆放。这种系统通过实时交互&…

创作里程碑:纪念日回顾与展望

目录 机缘&#xff1a;创作者初心 1. 实战项目 2. 日常学习 3. 技术交流 4. 总结 收获&#xff1a;创作者动力 创作与工作、学习的关系 憧憬&#xff1a;职业规划与创作规划 职业规划&#xff1a; 创作规划&#xff1a; 机缘&#xff1a;创作者初心 回望自己踏上…

软考(中级-软件设计师)数据库篇(1101)

第6章 数据库系统基础知识 一、基本概念 1、数据库 数据库&#xff08;Database &#xff0c;DB&#xff09;是指长期存储在计算机内的、有组织的、可共享的数据集合。数据库中的数据按一定的数据模型组织、描述和存储&#xff0c;具有较小的冗余度、较高的数据独立性和扩展…

go-zero 的使用

目录 1. 生成 user api 服务 2. 生成 user rpc 服务 3. 生成 user model 模型 4. 编写 user rpc 服务 1 修改配置文件 user.yaml 2 添加 user model 依赖 3 添加用户登录逻辑 Login 5. 编写 user api 服务 1 修改配置文件user.yaml 2 添加 user rpc 依赖 3 添加用户…

基金委:目前资助率过低,危害大,应提升至30~35%,增大青年、面上项目经费

国家基金委 近日&#xff0c;国家基金委发表题为《近十年 NSF资助率和资助强度上升 对我国科学基金资助工作的启示》文章&#xff0c;文章基于近十年 NSF总体资助变化,提出对我国科学基金资助工作启示&#xff0c;阐述了国家自然基金项目资助率下降情况、危害&#xff0c;并提…

NPM 包开发与优化全面指南

前言 Hey, 我是 Immerse系列文章首发于【Immerse】&#xff0c;更多内容请关注该网站转载说明&#xff1a;转载请注明原文出处及版权声明&#xff01; 1. 理解 NPM 包的结构 1.1 package.json 文件&#xff1a;包的核心 package.json文件是 NPM 包的中央配置&#xff0c;定…

学Linux的第六天

目录 账户和组管理 工作组管理 创建工作组groupadd 修改工作组groupmod 添加/删除组成员gpasswd 删除工作组groupdel 查看用户登录系统的情况 users查看当前登录系统的用户 last命令 lastlog命令 w命令 显示登录到系统的用户信息 who命令 Linux文件系统权限 文件…