<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hans-CN">
	<id>https://wiki.linuxsa.org/index.php?action=history&amp;feed=atom&amp;title=2025py%E9%87%8D%E5%AD%A6%E8%AE%A1%E5%88%92</id>
	<title>2025py重学计划 - 版本历史</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.linuxsa.org/index.php?action=history&amp;feed=atom&amp;title=2025py%E9%87%8D%E5%AD%A6%E8%AE%A1%E5%88%92"/>
	<link rel="alternate" type="text/html" href="https://wiki.linuxsa.org/index.php?title=2025py%E9%87%8D%E5%AD%A6%E8%AE%A1%E5%88%92&amp;action=history"/>
	<updated>2026-04-17T08:44:25Z</updated>
	<subtitle>本wiki上该页面的版本历史</subtitle>
	<generator>MediaWiki 1.43.1</generator>
	<entry>
		<id>https://wiki.linuxsa.org/index.php?title=2025py%E9%87%8D%E5%AD%A6%E8%AE%A1%E5%88%92&amp;diff=33&amp;oldid=prev</id>
		<title>Evan：​/* other */</title>
		<link rel="alternate" type="text/html" href="https://wiki.linuxsa.org/index.php?title=2025py%E9%87%8D%E5%AD%A6%E8%AE%A1%E5%88%92&amp;diff=33&amp;oldid=prev"/>
		<updated>2025-05-19T03:12:55Z</updated>

		<summary type="html">&lt;p&gt;&lt;span class=&quot;autocomment&quot;&gt;other&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;新页面&lt;/b&gt;&lt;/p&gt;&lt;div&gt;[[category:python]] [[category:devops]] &lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
=Day 1=&lt;br /&gt;
Day 1	Python 语法入门	变量、print、类型、输入输出	写一个 CLI 工具，输入姓名打印问候语&lt;br /&gt;
&lt;br /&gt;
写一个 CLI 工具，输入姓名打印问候语&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v1 cat  echo.py &lt;br /&gt;
name = &amp;#039;evan&amp;#039;&lt;br /&gt;
print( name + &amp;quot;  have a good day&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
v2&lt;br /&gt;
➜  py2025 cat echo.py &lt;br /&gt;
name = input(&amp;quot;please input your name&amp;quot;)&lt;br /&gt;
print( name + &amp;quot;  have a good day&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
 py  echo.py &lt;br /&gt;
please input your name evan&lt;br /&gt;
 evan  have a good day&lt;br /&gt;
➜  py2025 cat echo.py &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
v3&lt;br /&gt;
import sys&lt;br /&gt;
if  len(sys.argv) &amp;lt; 2:&lt;br /&gt;
    print(&amp;quot;Usage: python echo.py &amp;lt;name&amp;gt;&amp;quot;)&lt;br /&gt;
else:&lt;br /&gt;
    name = sys.argv[1]&lt;br /&gt;
    print(f&amp;quot;Hi,  {name}    have a good day&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
➜  py2025 py  echo.py &lt;br /&gt;
Usage: python echo.py &amp;lt;name&amp;gt;&lt;br /&gt;
➜  py2025 py  echo.py  evan&lt;br /&gt;
Hi,  evan    have a good d&lt;br /&gt;
&lt;br /&gt;
v4 &lt;br /&gt;
➜  py2025 cat greet.py &lt;br /&gt;
import argparse &lt;br /&gt;
def main():&lt;br /&gt;
    parser = argparse.ArgumentParser(description=&amp;quot;Say Hi to someone&amp;quot;)&lt;br /&gt;
    parser.add_argument(&amp;quot;-n&amp;quot;,&amp;quot;--name&amp;quot;,required=True,help=&amp;quot;The name of the person to greet.&amp;quot;)&lt;br /&gt;
    args = parser.parse_args()&lt;br /&gt;
    print(f&amp;quot;Hello,{args.name} hope you have a  good day&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    main()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
➜  py2025 python greet.py -n evan &lt;br /&gt;
Hello,evan hope you have a  good day&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=day2=&lt;br /&gt;
Day 2	流程控制	if, for, while, break, continue	判断磁盘使用率是否超过阈值&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
num = [1,2,3,4,5,6,]&lt;br /&gt;
for nu in num:&lt;br /&gt;
    if  nu % 2 == 0:&lt;br /&gt;
        print(f&amp;quot;{num} 偶数&amp;quot;)&lt;br /&gt;
        continue&lt;br /&gt;
    elif nu == 5:&lt;br /&gt;
        print(f&amp;quot;找到 5 quit loop&amp;quot;)&lt;br /&gt;
        break&lt;br /&gt;
    print(f&amp;quot;{num} 奇数&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=day3=&lt;br /&gt;
Day 3	数据结构	list, dict, set, tuple 常用操作	把 log 中的错误代码用 dict 统计次数&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  py2025 cat errors.log &lt;br /&gt;
ERR001&lt;br /&gt;
ERR002&lt;br /&gt;
ERR001&lt;br /&gt;
ERR003&lt;br /&gt;
ERR002&lt;br /&gt;
ERR001&lt;br /&gt;
➜  py2025 py 1.py &lt;br /&gt;
ERR001: 3次&lt;br /&gt;
ERR002: 2次&lt;br /&gt;
ERR003: 1次&lt;br /&gt;
➜  py2025 tail  1.py &lt;br /&gt;
error_counts = {}&lt;br /&gt;
with open(&amp;quot;errors.log&amp;quot;,&amp;quot;r&amp;quot;) as file:&lt;br /&gt;
    for line in file:&lt;br /&gt;
        error_code =  line.strip()&lt;br /&gt;
        error_counts[error_code] = error_counts.get(error_code,0) + 1&lt;br /&gt;
&lt;br /&gt;
for code,count in error_counts.items():&lt;br /&gt;
    print(f&amp;quot;{code}: {count}次&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
这ai 解说还真的有意思 &lt;br /&gt;
&lt;br /&gt;
for code, count in error_counts.items(): 这一行：&lt;br /&gt;
&lt;br /&gt;
    error_counts 是一个字典，用于存储错误代码及其出现的次数。例如，error_counts 可能是 {&amp;quot;E001&amp;quot;: 3, &amp;quot;E002&amp;quot;: 2} 这样的形式。&lt;br /&gt;
    items() 是字典的一个方法，它会返回一个包含多个元组的可迭代对象，每个元组由字典的键和对应的值组成。对于 {&amp;quot;E001&amp;quot;: 3, &amp;quot;E002&amp;quot;: 2} 这个字典，error_counts.items() 的结果就是 [(&amp;quot;E001&amp;quot;, 3), (&amp;quot;E002&amp;quot;, 2)] 这样的形式。&lt;br /&gt;
    for code, count in error_counts.items(): 是一个循环语句，code 和 count 是自定义的变量名。在每次循环中，code 会被赋值为字典的键（也就是错误代码，如 &amp;quot;E001&amp;quot;），count 会被赋值为对应的值（也就是该错误代码出现的次数，如 3）。也就是说，第一次循环时，code 是 &amp;quot;E001&amp;quot;，count 是 3；第二次循环时，code 是 &amp;quot;E002&amp;quot;，count 是 2。&lt;br /&gt;
&lt;br /&gt;
print(f&amp;quot;{code}: {count} 次&amp;quot;) 这一行：&lt;br /&gt;
&lt;br /&gt;
    print() 是 Python 内置的函数，用于在控制台输出信息。&lt;br /&gt;
    f&amp;quot;{code}: {count} 次&amp;quot; 是一个格式化字符串（也称为 f-string）。在这个字符串中，{code} 和 {count} 是占位符，会被 code 和 count 变量的值所替换。例如，当 code 是 &amp;quot;E001&amp;quot;，count 是 3 时，f&amp;quot;{code}: {count} 次&amp;quot; 就会被解析为 &amp;quot;E001: 3 次&amp;quot;，然后 print() 函数会将这个字符串输出到控制台。&lt;br /&gt;
&lt;br /&gt;
第 7 行：error_code = line.strip()&lt;br /&gt;
&lt;br /&gt;
    strip() 方法移除字符串首尾的空白字符（如换行符 \n、空格）。&lt;br /&gt;
    示例：&lt;br /&gt;
        &amp;quot;E001\n&amp;quot;.strip() → &amp;quot;E001&amp;quot;&lt;br /&gt;
        &amp;quot; E002 &amp;quot;.strip() → &amp;quot;E002&amp;quot;&lt;br /&gt;
&lt;br /&gt;
第 9 行：error_counts[error_code] = error_counts.get(error_code, 0) + 1&lt;br /&gt;
&lt;br /&gt;
    核心逻辑：统计错误代码的出现次数。&lt;br /&gt;
    get(key, default) 方法：&lt;br /&gt;
        如果 key 存在于字典中，返回对应的值。&lt;br /&gt;
        如果 key 不存在，返回默认值 default（这里是 0）。&lt;br /&gt;
    执行过程：&lt;br /&gt;
        第一次遇到 &amp;quot;E001&amp;quot;：&lt;br /&gt;
            error_counts.get(&amp;quot;E001&amp;quot;, 0) 返回 0&lt;br /&gt;
            0 + 1 = 1，因此 error_counts[&amp;quot;E001&amp;quot;] = 1&lt;br /&gt;
        第二次遇到 &amp;quot;E001&amp;quot;：&lt;br /&gt;
            error_counts.get(&amp;quot;E001&amp;quot;, 0) 返回 1&lt;br /&gt;
            1 + 1 = 2，因此 error_counts[&amp;quot;E001&amp;quot;] = 2&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=day 4=&lt;br /&gt;
Day 4	函数与作用域	定义函数、传参、返回值、局部变量	封装一个“计算服务器负载”的函数&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def  calculate_server_load(cpu_usage,memory_used,disk_io, total_memory=32,max_io=32):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    計算伺服器負載。&lt;br /&gt;
    參數：&lt;br /&gt;
        cpu_usage (float): CPU 使用率（%）&lt;br /&gt;
        memory_used (float): 記憶體使用量（GB）&lt;br /&gt;
        disk_io (float): 磁碟 I/O 速率（MB/s）&lt;br /&gt;
        total_memory (float): 總記憶體（GB，預設 32）&lt;br /&gt;
        max_io (float): 最大 I/O 速率（MB/s，預設 200）&lt;br /&gt;
    返回：&lt;br /&gt;
        float: 伺服器負載（%）&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    if not all(isinstance(x,(int,float)) for x in  [cpu_usage,memory_used, disk_io, total_memory,max_io]):&lt;br /&gt;
        raise ValueError(&amp;quot;參數必須為數字&amp;quot;)&lt;br /&gt;
    if cpu_usage &amp;lt; 0 or  memory_used &amp;lt; 0  or disk_io &amp;lt; 0  or total_memory &amp;lt;=  0 or max_io &amp;lt;= 0:&lt;br /&gt;
        raise  ValueError(&amp;quot;參數不能為負數，且總記憶體與最大 I/O 不能為 0 &amp;quot;)&lt;br /&gt;
    if  cpu_usage &amp;gt; 100:&lt;br /&gt;
        raise ValueError(&amp;quot;CPU 使用率不能大於 100%&amp;quot;)&lt;br /&gt;
    if  memory_used &amp;gt; total_memory:&lt;br /&gt;
        raise ValueError(&amp;quot;記憶體使用量不能大於總記憶體&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    cpu_load = cpu_usage * 0.4 &lt;br /&gt;
    memory_load = (memory_used / total_memory * 100) * 0.4&lt;br /&gt;
    io_load = (disk_io / max_io * 100) * 0.2&lt;br /&gt;
    total_load = cpu_load + memory_load + io_load &lt;br /&gt;
&lt;br /&gt;
    return round(total_load,2)&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    load = calculate_server_load(80,20,100,32,200)&lt;br /&gt;
    print(f&amp;quot;伺服器負載為 {load}%&amp;quot;)&lt;br /&gt;
except ValueError as e:&lt;br /&gt;
    print(f&amp;quot;err{e}&amp;quot;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=day 5=&lt;br /&gt;
Day 5	文件操作	with open, 读写、逐行处理	写一个“读取Nginx日志并分析IP访问量”的脚本&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
from collections  import Counter &lt;br /&gt;
import sys  &lt;br /&gt;
&lt;br /&gt;
if len(sys.argv) &amp;lt; 2:&lt;br /&gt;
    print(&amp;quot;Usage: python nglog.py log_file&amp;quot;)&lt;br /&gt;
    sys.exit(1)&lt;br /&gt;
&lt;br /&gt;
log_file = sys.argv[1]&lt;br /&gt;
ip_counter = Counter()&lt;br /&gt;
&lt;br /&gt;
try:&lt;br /&gt;
    with open(log_file,&amp;#039;r&amp;#039;) as file:&lt;br /&gt;
        for line in file:&lt;br /&gt;
            if line.strip():&lt;br /&gt;
                ip = line.lstrip().split(&amp;#039; &amp;#039;,1)[0]&lt;br /&gt;
                ip_counter[ip] += 1&lt;br /&gt;
except FileNotFoundError:&lt;br /&gt;
    print(f&amp;quot;Error: file &amp;#039;{log_file}&amp;#039; not found.&amp;quot;)&lt;br /&gt;
    sys.exit(1)&lt;br /&gt;
&lt;br /&gt;
print(&amp;quot;IP 访问量统计&amp;quot;)&lt;br /&gt;
for ip, count in ip_counter.most_common():&lt;br /&gt;
    print(f&amp;quot;IP: {ip}, Accesses: {count}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# IP 地址提取：&lt;br /&gt;
&lt;br /&gt;
#     line.lstrip().split(&amp;#039; &amp;#039;, 1)[0]：&lt;br /&gt;
#         lstrip() 去除行首空白字符&lt;br /&gt;
#         split(&amp;#039; &amp;#039;, 1) 按第一个空格分割字符串&lt;br /&gt;
#         [0] 取分割后的第一个部分（即 IP 地址）&lt;br /&gt;
&lt;br /&gt;
#  cat access.log &lt;br /&gt;
# 192.168.1.1 - - [01/Jan/2023:00:00:00 +0000] &amp;quot;GET /index.html HTTP/1.1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# python nglog.py access.log &lt;br /&gt;
# IP 访问量统计&lt;br /&gt;
# IP: 192.168.1.1, Accesses: 2&lt;br /&gt;
# IP: 192.168.1.2, Accesses: 1&lt;br /&gt;
&lt;br /&gt;
for ip, count in ip_counter.most_common():&lt;br /&gt;
ip_counter.most_common() 的作用&lt;br /&gt;
&lt;br /&gt;
    ip_counter 是一个 Counter 对象，它存储了每个 IP 地址的出现次数。&lt;br /&gt;
        例如：Counter({&amp;#039;192.168.1.1&amp;#039;: 5, &amp;#039;192.168.1.2&amp;#039;: 3, &amp;#039;10.0.0.1&amp;#039;: 2})&lt;br /&gt;
    most_common() 是 Counter 的方法，用于按计数从高到低排序元素。&lt;br /&gt;
        返回值：一个包含元组的列表，每个元组格式为 (元素, 计数)。&lt;br /&gt;
        例如：[(&amp;#039;192.168.1.1&amp;#039;, 5), (&amp;#039;192.168.1.2&amp;#039;, 3), (&amp;#039;10.0.0.1&amp;#039;, 2)]&lt;br /&gt;
&lt;br /&gt;
循环变量解构&lt;br /&gt;
&lt;br /&gt;
    for ip, count in ...：&lt;br /&gt;
        ip：每次循环中存储当前 IP 地址（元组的第一个元素）。&lt;br /&gt;
        count：存储该 IP 的访问次数（元组的第二个元素）。&lt;br /&gt;
&lt;br /&gt;
第 3 行：print(f&amp;quot;IP: {ip}, Accesses: {count}&amp;quot;)&lt;br /&gt;
f-string 格式化输出&lt;br /&gt;
&lt;br /&gt;
    语法：f&amp;quot;字符串{变量}&amp;quot; 会将 {} 内的变量值插入字符串。&lt;br /&gt;
    示例：&lt;br /&gt;
        当 ip = &amp;#039;192.168.1.1&amp;#039; 且 count = 5 时，输出：&lt;br /&gt;
        plaintext&lt;br /&gt;
&lt;br /&gt;
        IP: 192.168.1.1, Accesses: 5&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
完整执行流程示例&lt;br /&gt;
假设 ip_counter 的内容为：&lt;br /&gt;
&lt;br /&gt;
python&lt;br /&gt;
运行&lt;br /&gt;
&lt;br /&gt;
Counter({&amp;#039;192.168.1.1&amp;#039;: 3, &amp;#039;10.0.0.1&amp;#039;: 2, &amp;#039;192.168.1.100&amp;#039;: 1})&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    第一次循环：&lt;br /&gt;
        ip = &amp;#039;192.168.1.1&amp;#039;，count = 3&lt;br /&gt;
        输出：IP: 192.168.1.1, Accesses: 3&lt;br /&gt;
    第二次循环：&lt;br /&gt;
        ip = &amp;#039;10.0.0.1&amp;#039;，count = 2&lt;br /&gt;
        输出：IP: 10.0.0.1, Accesses: 2&lt;br /&gt;
    第三次循环：&lt;br /&gt;
        ip = &amp;#039;192.168.1.100&amp;#039;，count = 1&lt;br /&gt;
        输出：IP: 192.168.1.100, Accesses: 1&lt;br /&gt;
&lt;br /&gt;
为什么用 most_common()？&lt;br /&gt;
&lt;br /&gt;
    默认行为：most_common() 不指定参数时，会返回所有元素，并按计数从高到低排序。&lt;br /&gt;
    等价写法：&lt;br /&gt;
    python&lt;br /&gt;
&lt;br /&gt;
运行&lt;br /&gt;
&lt;br /&gt;
# 手动排序（效果相同）&lt;br /&gt;
for ip, count in sorted(ip_counter.items(), key=lambda x: x[1], reverse=True):&lt;br /&gt;
    print(f&amp;quot;IP: {ip}, Accesses: {count}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
最终输出示例&lt;br /&gt;
plaintext&lt;br /&gt;
&lt;br /&gt;
IP 访问量统计&lt;br /&gt;
IP: 192.168.1.1, Accesses: 3&lt;br /&gt;
IP: 10.0.0.1, Accesses: 2&lt;br /&gt;
IP: 192.168.1.100, Accesses: 1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
这两行代码的核心作用是将统计结果以易读的格式展示出来，并按访问量从高到低排序，帮助用户快速定位高频访问的 IP 地址。&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=day 6=&lt;br /&gt;
Day 6	错误处理	try...except...finally	模拟连接失败的错误处理&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import time &lt;br /&gt;
def connect_to_resource(url,max_retries=3):&lt;br /&gt;
    retries = 0&lt;br /&gt;
    while  retries &amp;lt; max_retries:&lt;br /&gt;
        try:&lt;br /&gt;
            print(f&amp;quot;尝试连接到 {url}, 第 {retries+1} 次尝试&amp;quot;)&lt;br /&gt;
            should_fail =  (retries &amp;lt; 2)&lt;br /&gt;
            if should_fail:&lt;br /&gt;
                raise Exception(&amp;quot;Failed to connect to resource&amp;quot;)&lt;br /&gt;
            else:&lt;br /&gt;
                print(&amp;quot;连接成功 {url}&amp;quot;)&lt;br /&gt;
                return True &lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            print(f&amp;quot;连接失败 {url}, 错误信息: {e}&amp;quot;)&lt;br /&gt;
            retries += 1&lt;br /&gt;
            print(f&amp;quot;等 2 秒后重试&amp;quot;)&lt;br /&gt;
            time.sleep(1)&lt;br /&gt;
        finally:&lt;br /&gt;
            print(&amp;quot;无论连接成功与不，这里都会执行清理操作&amp;quot;)&lt;br /&gt;
            print(&amp;quot;-&amp;quot; * 20)&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;尝试了 {max_retries} 次，连接失败&amp;quot;)&lt;br /&gt;
    return False &lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    resource_url = &amp;quot;https://wiki.linuxchina.net&amp;quot;&lt;br /&gt;
    connection_successful = connect_to_resource(resource_url)&lt;br /&gt;
&lt;br /&gt;
    if connection_successful:&lt;br /&gt;
        print(&amp;quot;连接成功&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(&amp;quot;连接失败&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
&lt;br /&gt;
尝试连接到 https://wiki.linuxchina.net, 第 1 次尝试&lt;br /&gt;
连接失败 https://wiki.linuxchina.net, 错误信息: Failed to connect to resource&lt;br /&gt;
等 2 秒后重试&lt;br /&gt;
无论连接成功与不，这里都会执行清理操作&lt;br /&gt;
--------------------&lt;br /&gt;
尝试连接到 https://wiki.linuxchina.net, 第 2 次尝试&lt;br /&gt;
连接失败 https://wiki.linuxchina.net, 错误信息: Failed to connect to resource&lt;br /&gt;
等 2 秒后重试&lt;br /&gt;
无论连接成功与不，这里都会执行清理操作&lt;br /&gt;
--------------------&lt;br /&gt;
尝试连接到 https://wiki.linuxchina.net, 第 3 次尝试&lt;br /&gt;
连接成功 {url}&lt;br /&gt;
无论连接成功与不，这里都会执行清理操作&lt;br /&gt;
--------------------&lt;br /&gt;
连接成功&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=day 7=&lt;br /&gt;
Day 7	模块与包	import, sys.path, 自定义模块	把昨天的脚本封装为一个模块&lt;br /&gt;
&lt;br /&gt;
=day 8=&lt;br /&gt;
Day 8	subprocess 与 os	执行 shell 命令，获取结果	写一个“批量检测服务状态”的脚本&lt;br /&gt;
&lt;br /&gt;
== [[利用python检测远程 IP和端口是否可连接并钉钉报警|socket version 利用python检测远程 IP和端口是否可连接并钉钉报警]]==&lt;br /&gt;
== nc version==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
import subprocess &lt;br /&gt;
&lt;br /&gt;
def check_port(host,port):&lt;br /&gt;
    try:&lt;br /&gt;
        result = subprocess.run(&lt;br /&gt;
            [&amp;#039;nc&amp;#039;,&amp;#039;-zv&amp;#039;,host,str(port)],&lt;br /&gt;
            stdout = subprocess.PIPE,&lt;br /&gt;
            stderr = subprocess.STDOUT,&lt;br /&gt;
            timeout = 3, &lt;br /&gt;
            text=True&lt;br /&gt;
        )&lt;br /&gt;
        if &amp;quot;succeeded&amp;quot; in result.stdout or &amp;quot;open&amp;quot; in result.stdout:&lt;br /&gt;
            return f&amp;quot;{host}:{port} is UP&amp;quot;&lt;br /&gt;
        else:&lt;br /&gt;
            return f&amp;quot;{host}:{port} is DOWN&amp;quot;&lt;br /&gt;
    except subprocess.TimeoutExpired:&lt;br /&gt;
        return f&amp;quot;{host}:{port} Timeout&amp;quot;&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
        return f&amp;quot;{host}:{port} Error - {str(e)}&amp;quot;&lt;br /&gt;
    &lt;br /&gt;
if __name__ ==&amp;#039;__main__&amp;#039;:&lt;br /&gt;
    targets = [&lt;br /&gt;
        (&amp;#039;127.0.0.1&amp;#039;,22),&lt;br /&gt;
        (&amp;#039;8.8.8.8&amp;#039;,53),&lt;br /&gt;
        (&amp;#039;www.bing.com&amp;#039;,80),&lt;br /&gt;
    ]&lt;br /&gt;
    for host, port in targets:&lt;br /&gt;
        print(check_port(host,port))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
 py check_port.py     &lt;br /&gt;
127.0.0.1:22 is UP&lt;br /&gt;
8.8.8.8:53 is UP&lt;br /&gt;
www.bing.com:80 is UP&lt;br /&gt;
&lt;br /&gt;
socket version and telnet version&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== telnet version==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import telnetlib &lt;br /&gt;
import socket &lt;br /&gt;
&lt;br /&gt;
def check_port(host,port,timeout=3):&lt;br /&gt;
    try:&lt;br /&gt;
        with telnetlib.Telnet(host,port,timeout):&lt;br /&gt;
            return True &lt;br /&gt;
    except socket.timeout:&lt;br /&gt;
        return False&lt;br /&gt;
    except Exception:&lt;br /&gt;
        return False&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;#039;__main__&amp;#039;:&lt;br /&gt;
    targets = [&lt;br /&gt;
        (&amp;quot;127.0.0.1&amp;quot;,22),&lt;br /&gt;
        (&amp;quot;linuxsa.org&amp;quot;,80)&lt;br /&gt;
    ]&lt;br /&gt;
&lt;br /&gt;
    for host,port in targets:&lt;br /&gt;
        status = &amp;quot;UP&amp;quot; if check_port(host,port) else &amp;quot;DOWN&amp;quot;&lt;br /&gt;
        print(f&amp;quot;{host}:{port} is {status}&amp;quot;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
===telnet 报警版 ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import telnetlib &lt;br /&gt;
import socket &lt;br /&gt;
import yaml &lt;br /&gt;
import requests &lt;br /&gt;
import json &lt;br /&gt;
from datetime import datetime &lt;br /&gt;
&lt;br /&gt;
DING_WEBHOOK =&amp;#039;https://oapi.dingtalk.com/robot/send?access_token=4578bcd63ad05f3a315ef4f971c70efe9990ef0adc47f804c3273371a73d&amp;#039;&lt;br /&gt;
&lt;br /&gt;
def load_from_yaml(file_path):&lt;br /&gt;
    with open(file_path,&amp;#039;r&amp;#039;) as f:&lt;br /&gt;
        data = yaml.safe_load(f)&lt;br /&gt;
        return data.get(&amp;quot;targets&amp;quot;,[])&lt;br /&gt;
    &lt;br /&gt;
def send_ding_alert(message):&lt;br /&gt;
    headers = {&amp;#039;Content-Type&amp;#039;: &amp;#039;application/json; charset=utf-8&amp;#039;}&lt;br /&gt;
    data = {&lt;br /&gt;
        &amp;quot;msgtype&amp;quot;: &amp;quot;text&amp;quot;,&lt;br /&gt;
        &amp;quot;text&amp;quot;: {&lt;br /&gt;
            &amp;quot;content&amp;quot;: message&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    try:&lt;br /&gt;
        response = requests.post(DING_WEBHOOK, headers=headers, data=json.dumps(data),timeout=5)&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
        print(f&amp;quot;Failed to send ding alert: {e}&amp;quot;)&lt;br /&gt;
       &lt;br /&gt;
 &lt;br /&gt;
def check_port(host,port,timeout=3):&lt;br /&gt;
    try:&lt;br /&gt;
        with telnetlib.Telnet(host,port,timeout):&lt;br /&gt;
            return True &lt;br /&gt;
    except socket.timeout:&lt;br /&gt;
        return False&lt;br /&gt;
    except Exception:&lt;br /&gt;
        return False&lt;br /&gt;
def main():&lt;br /&gt;
    ips = load_from_yaml(&amp;quot;/home/evan/data/tmp/py2025/ips.yaml&amp;quot;)&lt;br /&gt;
    down_list=[]&lt;br /&gt;
&lt;br /&gt;
    for item in ips:&lt;br /&gt;
        host = item.get(&amp;quot;host&amp;quot;)&lt;br /&gt;
        port = item.get(&amp;quot;port&amp;quot;)&lt;br /&gt;
        if not check_port(host,port):&lt;br /&gt;
            down_list.append(f&amp;quot;{host}:{port}&amp;quot;)&lt;br /&gt;
    &lt;br /&gt;
    if down_list:&lt;br /&gt;
        now = datetime.now().strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;)&lt;br /&gt;
        message = f&amp;quot;[{now}]  Port detection anomaly端口检测异常 {now} 报警: {&amp;#039;, &amp;#039;.join(down_list)} 🚨&amp;quot;&lt;br /&gt;
        send_ding_alert(message)&lt;br /&gt;
    else:&lt;br /&gt;
        print(&amp;quot;All ports are up&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;#039;__main__&amp;#039;:&lt;br /&gt;
    main()&lt;br /&gt;
&lt;br /&gt;
ips.yaml &lt;br /&gt;
targets:&lt;br /&gt;
  - host: 127.0.0.1&lt;br /&gt;
    port: 24&lt;br /&gt;
  - host: 8.8.8.8&lt;br /&gt;
    port: 53&lt;br /&gt;
  - host: www.bing.com&lt;br /&gt;
    port: 89&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=day 9=&lt;br /&gt;
Day 9	requests 库	API 请求、返回解析、header/cookie	用钉钉/企业微信接口推送消息&lt;br /&gt;
&lt;br /&gt;
pre:&lt;br /&gt;
都是图形操作，你事先有个钉钉群,没有的话创建一个&lt;br /&gt;
进入钉钉群 → 添加一个「自定义机器人」&lt;br /&gt;
&lt;br /&gt;
设置关键词（如：报警）&lt;br /&gt;
&lt;br /&gt;
复制 Webhook 地址（例子如下）&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import requests &lt;br /&gt;
import json &lt;br /&gt;
&lt;br /&gt;
webhook_url = &amp;#039;https://oapi.dingtalk.com/robot/send?access_token=6678bcd63ad05f3a315ef4f971c70efe9990ef0adc47f804c3273371a73dc155&amp;#039;&lt;br /&gt;
&lt;br /&gt;
message_text = &amp;quot;报警: 服务器磁盘使用率超过阈值！🚨- test message&amp;quot;&lt;br /&gt;
&lt;br /&gt;
headers = {&lt;br /&gt;
    &amp;quot;Content-Type&amp;quot;: &amp;quot;application/json&amp;quot;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
payload = {&lt;br /&gt;
    &amp;quot;msgtype&amp;quot;: &amp;quot;text&amp;quot;,&lt;br /&gt;
    &amp;quot;text&amp;quot;: {&lt;br /&gt;
        &amp;quot;content&amp;quot;:message_text &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
response = requests.post(url=webhook_url,headers=headers, data=json.dumps(payload))&lt;br /&gt;
&lt;br /&gt;
if response.status_code == 200:&lt;br /&gt;
    result = response.json()&lt;br /&gt;
    if result.get(&amp;quot;errcode&amp;quot;) == 0:&lt;br /&gt;
        print(&amp;quot;message sent successfully&amp;quot;)&lt;br /&gt;
    else:&lt;br /&gt;
        print(&amp;quot;message sent failed, error code: &amp;quot;, result)&lt;br /&gt;
else:&lt;br /&gt;
    print(&amp;quot;HTTP err message sent failed, status code: &amp;quot;, response.status_code)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Day 10	=&lt;br /&gt;
paramiko + fabric	SSH 自动执行远程命令	写一个“批量部署脚本”上传+执行&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
from fabric import Connection &lt;br /&gt;
from invoke import Responder  &lt;br /&gt;
&lt;br /&gt;
servers = [&lt;br /&gt;
    {&amp;quot;host&amp;quot;: &amp;quot;192.168.10.5&amp;quot;, &amp;quot;user&amp;quot;: &amp;quot;root&amp;quot;, &amp;quot;password&amp;quot;: &amp;quot;xxxxxxx&amp;quot;},&lt;br /&gt;
    {&amp;quot;host&amp;quot;: &amp;quot;192.168.1.101&amp;quot;, &amp;quot;user&amp;quot;: &amp;quot;root&amp;quot;, &amp;quot;password&amp;quot;: &amp;quot;123456&amp;quot;},&lt;br /&gt;
&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
local_script = &amp;quot;test36.sh&amp;quot;&lt;br /&gt;
remote_path = &amp;quot;/tmp/test36.sh&amp;quot;&lt;br /&gt;
&lt;br /&gt;
remote_cmd = f&amp;quot;bash {remote_path}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
for server in servers:&lt;br /&gt;
    print(f&amp;quot;uploading script to {server[&amp;#039;host&amp;#039;]}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    conn = Connection(&lt;br /&gt;
        host=server[&amp;#039;host&amp;#039;],&lt;br /&gt;
        user=server[&amp;#039;user&amp;#039;],&lt;br /&gt;
        connect_kwargs={&amp;quot;password&amp;quot;: server[&amp;#039;password&amp;#039;]}&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;uploading {local_script} to {remote_path}&amp;quot;)&lt;br /&gt;
    conn.put(local_script, remote_path)&lt;br /&gt;
&lt;br /&gt;
    conn.run(f&amp;quot;chmod +x {remote_path}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    print(&amp;quot;running remote script&amp;quot;)&lt;br /&gt;
    result = conn.run(remote_cmd,hide=True)&lt;br /&gt;
    print(result.stdout)&lt;br /&gt;
&lt;br /&gt;
    conn.close()&lt;br /&gt;
&lt;br /&gt;
    print(f&amp;quot;Done with {server[&amp;#039;host&amp;#039;]}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# cat /tmp/test36.sh &lt;br /&gt;
#!/bin/bash&lt;br /&gt;
echo &amp;quot;Deploying service...&amp;quot;&lt;br /&gt;
# 示例命令，可改为拉 Git、重启服务等&lt;br /&gt;
systemctl restart nginx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==trouble shooting ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    from .loader import FilesystemLoader  # noqa&lt;br /&gt;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^&lt;br /&gt;
  File &amp;quot;/usr/lib/python3/dist-packages/invoke/loader.py&amp;quot;, line 3, in &amp;lt;module&amp;gt;&lt;br /&gt;
    import imp&lt;br /&gt;
ModuleNotFoundError: No module named &amp;#039;imp&amp;#039;&lt;br /&gt;
&lt;br /&gt;
你使用的 Fabric 版本太老，不兼容你当前的 Python 版本（Python 3.12+ 已经移除了 imp 模块）。&lt;br /&gt;
&lt;br /&gt;
pip3 install --upgrade fabric invoke&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Day 11	=&lt;br /&gt;
argparse 与定时任务	解析命令参数、结合 crontab	写一个“CLI磁盘告警工具”定时运行&lt;br /&gt;
=Day 12=&lt;br /&gt;
	JSON/YAML 配置文件	json, yaml，配置与数据保存	写一个可配置的“日志清理工具”&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import os  &lt;br /&gt;
import glob &lt;br /&gt;
import time  &lt;br /&gt;
import argparse &lt;br /&gt;
import json &lt;br /&gt;
import  yaml &lt;br /&gt;
from datetime import datetime,timedelta &lt;br /&gt;
&lt;br /&gt;
def load_config(file_path):&lt;br /&gt;
    with open(file_path, &amp;#039;r&amp;#039;) as f:&lt;br /&gt;
        if file_path.endswith(&amp;#039;.json&amp;#039;):&lt;br /&gt;
            return json.load(f)&lt;br /&gt;
        elif file_path.endswith(&amp;#039;.yaml&amp;#039;) or file_path.endswith(&amp;#039;.yml&amp;#039;):&lt;br /&gt;
            return yaml.safe_load(f)&lt;br /&gt;
        else:&lt;br /&gt;
            raise ValueError(&amp;#039;Unsupported file type&amp;#039;)&lt;br /&gt;
        &lt;br /&gt;
def clean_logs(path,pattern,keep_days):&lt;br /&gt;
    now = time.time()&lt;br /&gt;
    cutoff = now - (keep_days * 24 * 60 * 60)&lt;br /&gt;
    full_patern = os.path.join(path,pattern)&lt;br /&gt;
    deleted = []&lt;br /&gt;
&lt;br /&gt;
    for file in glob.glob(full_patern):&lt;br /&gt;
        if os.path.isfile(file):&lt;br /&gt;
            file_mtime = os.path.getmtime(file)&lt;br /&gt;
            if file_mtime &amp;lt; cutoff:&lt;br /&gt;
                os.remove(file)&lt;br /&gt;
                deleted.append(file)&lt;br /&gt;
&lt;br /&gt;
    return deleted &lt;br /&gt;
&lt;br /&gt;
def main():&lt;br /&gt;
    #创建一个命令行解析器对象 parser description 是给这个工具添加一个简短说明，在 --help 里会看到&lt;br /&gt;
    parser = argparse.ArgumentParser(description=&amp;quot;clean log files&amp;quot;) &lt;br /&gt;
&lt;br /&gt;
    #添加一个参数 --config（必须提供，required=True）&lt;br /&gt;
    parser.add_argument(&amp;#039;--config&amp;#039;,required=True,help=&amp;quot;配置文件路径 (.json/.yaml)&amp;quot;)&lt;br /&gt;
    args = parser.parse_args()&lt;br /&gt;
&lt;br /&gt;
    config = load_config(args.config)&lt;br /&gt;
    log_items = config.get(&amp;quot;log_paths&amp;quot;,[])&lt;br /&gt;
&lt;br /&gt;
    for item in log_items:&lt;br /&gt;
        path = item.get(&amp;quot;path&amp;quot;)&lt;br /&gt;
        pattern = item.get(&amp;quot;pattern&amp;quot;,&amp;quot;*.log&amp;quot;)&lt;br /&gt;
        keep_days = item.get(&amp;quot;keep_days&amp;quot;,7)&lt;br /&gt;
&lt;br /&gt;
        deleted = clean_logs(path,pattern,keep_days)&lt;br /&gt;
        for f in deleted:&lt;br /&gt;
            print(f&amp;quot;deleted {f}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    main()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#      sudo python  log_cleaner.py --config  config.yaml&lt;br /&gt;
# deleted /var/log/nginx/access.log&lt;br /&gt;
# deleted /var/log/nginx/error.log&lt;br /&gt;
# ➜  py2025 &lt;br /&gt;
&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
假设你的配置文件写的是：&lt;br /&gt;
&lt;br /&gt;
log_paths:&lt;br /&gt;
  - path: &amp;quot;/var/log/nginx&amp;quot;&lt;br /&gt;
    pattern: &amp;quot;*.log&amp;quot;&lt;br /&gt;
    keep_days: 5&lt;br /&gt;
  - path: &amp;quot;/var/log/app&amp;quot;&lt;br /&gt;
    keep_days: 10&lt;br /&gt;
&lt;br /&gt;
这段代码会这样处理：&lt;br /&gt;
&lt;br /&gt;
    第一个 item：path=&amp;quot;/var/log/nginx&amp;quot;，pattern=&amp;quot;*.log&amp;quot;，keep_days=5&lt;br /&gt;
&lt;br /&gt;
    第二个 item：path=&amp;quot;/var/log/app&amp;quot;，pattern=&amp;quot;*.log&amp;quot;（默认值），keep_days=10&lt;br /&gt;
&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Day 13=&lt;br /&gt;
	简易项目实战	综合使用上述技能	“自动部署 + 告警 + 日志备份”工具原型&lt;br /&gt;
=Day 14	=&lt;br /&gt;
回顾 + 面试准备	简述项目、准备英文表达	准备好一个 Python 项目介绍 + 中英文回答模板&lt;br /&gt;
=other=&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
import  os  &lt;br /&gt;
import time &lt;br /&gt;
&lt;br /&gt;
log_dir= &amp;quot;/var/log/nginx&amp;quot;&lt;br /&gt;
now = time.time()&lt;br /&gt;
&lt;br /&gt;
for filename in  os.listdir(log_dir):&lt;br /&gt;
    if filename.endswith(&amp;quot;.log&amp;quot;):&lt;br /&gt;
        filepath = os.path.join(log_dir,filename)&lt;br /&gt;
        if os.stat(filepath).st_mtime &amp;lt; now - 3600*24*7: # delete log files older than 7 days&lt;br /&gt;
            os.remove(filepath)&lt;br /&gt;
            print(f&amp;quot;Deleted {filepath}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#shell&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
#LOG_DIR=$1&lt;br /&gt;
LOG_DIR=&amp;quot;/var/log/apache2&amp;quot;&lt;br /&gt;
&lt;br /&gt;
find &amp;quot;$LOG_DIR&amp;quot; -name &amp;quot;*.log&amp;quot; -type f -mtime +7 -exec rm -f {} \;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Evan</name></author>
	</entry>
</feed>