
1. 100个日常实用脚本一次彻底的工具箱整理先说说我为什么会对“100个日常实用脚本”这个题目感兴趣。做了几年开发和运维电脑里的脚本从零零散撒十几个慢慢堆到上百个散落在各种目录、甚至聊天记录里。真正遇到问题时常常是“好像写过类似的东西但找不到、也记不清怎么用了”。后来痛定思痛花了一个周末把所有脚本捞出来重新分类、梳理、补齐参数和文档整理成了100个工具的集合才敢说“日常实用”这四个字。这篇内容不是教你把100个脚本从头写一遍那没有意义。我想分享的是这100个脚本是怎么选出来的、内部如何分类组织每一类里核心脚本的设计逻辑和关键代码片段以及部署到真实环境时的完整流程和踩坑记录。适合三类人看刚入门、想通过大量例子提升脚本能力的开发者手里脚本不少但乱成一团、想系统整理的老手以及想在公司或团队里推广标准化工具集的负责人。内容我尽量用大白话讲清楚偶尔上点代码保证你能直接照抄。2. 脚本体系怎么搭先想清楚再动手2.1 什么才算“日常实用”三条过滤标准100个脚本听起来很多实际上筛选过程非常苛刻。我从最初200多个候选脚本里砍掉一半才凑出“这100个值得留”的清单。筛选标准就三条分享给你参考至少被真实使用过三次以上。一次性脚本哪怕再精巧除非它解决了某个高频痛点否则直接删掉。判断依据很简单翻历史记录同一个脚本是否反复出现。单次运行时间不超过几分钟。日常工具脚本跑了十分钟还没出结果大部分人就没有耐心等第二次了。处理大数据量的任务宁可拆成多个小脚本阶段缓存也不要硬扛。不需要复杂的交互和人工干预。参数越少越好能默认就不用指定能自动检测就不让用户输入。命令行工具最怕的就是“你要先告诉我A再告诉我B”时间长了连作者本人都会忘。拿文件批量重命名举例我早期版本要求输入目录、输入规则、输出格式三个参数缺一不可结果用了两次就被自己弃了。后来改成默认处理当前目录、默认规则是“文件名里的空格替换成下划线”想自定义再传参数这才真正沉淀下来。2.2 整体目录结构按任务类型而不是按语言划分分类是整理这类集合时最让人纠结的部分。我的建议是按任务类型分目录而不是按语言分。很多新手喜欢建立python_scripts/、bash_scripts/这样的文件夹但真实使用时你关心的永远是“我要整理文件”“我要看服务器状态”而不是“我该找Python脚本还是Shell脚本”。我最终确定的顶层结构是这样的分类目录覆盖任务脚本数量filesystem/文件批量操作、磁盘空间分析、文件类型统计16text_processing/日志提取、编码转换、格式化输出、正则工具18system_ops/进程管理、资源监控、定时巡检、网络检查14data_processing/CSV处理、JSON校验、Excel读写、数据库小工具16office_auto/Word/PDF批量操作、Excel合并、素材整理12network_utils/连通性检测、端口扫描、HTTP接口探测、DNS查询8backup_sync/文件同步、增量备份、压缩归档、加密备份9dev_helpers/Git辅助、代码统计、环境变量管理、编码规范检查7合计100这种结构的优越性在长期使用时特别明显当你想找“批量压缩图片”的脚本不会纠结它该归到图片处理还是归档工具直接进backup_sync/翻一圈或者用统一入口搜关键词就行。目录本身就是一套记忆索引。2.3 命名规范脚本名必须是“动词对象”整理了100个脚本后我最大的体悟是给脚本起名决定了它能否被长期复用。好的脚本名应该像命令一样直接表达出“这个工具能干什么”。我的统一规范是动词_对象.py或动词_对象.sh小写加下划线不用驼峰。反例早期命名正例整理后命名tool1.pymerge_csv.pyfix.pyrestore_backup.shtest2.pycheck_port.pyfinal_version.pydedup_files.py同时每个脚本开头必须有一段固定的Docstring记录用途一句话说明、用法示例、依赖模块、维护日期。这样即使三个月没碰过某个脚本看一眼头部也能快速知道它是干什么的、怎么跑。3. 几类核心脚本的设计与实现细节3.1 文件批量处理类整理下载目录的实战范例文件整理类脚本是我日常使用频率最高的类别。filesystem/目录下的核心脚本是organize_downloads.py功能是把乱七八糟的下载目录按文件扩展名自动分类归档到不同子目录。实现思路是用Python的pathlib遍历目录根据后缀名映射到目标类别遇到同名文件自动加序号防止覆盖。关键代码只有三四十行但有几个细节是真正决定成败的地方from pathlib import Path import shutil import re # 扩展名到类别目录的映射表 CATEGORY_MAP { images: {.jpg, .jpeg, .png, .gif, .bmp, .svg, .webp}, documents: {.pdf, .doc, .docx, .txt, .md, .ppt, .pptx}, sheets: {.xls, .xlsx, .csv, .tsv}, archives: {.zip, .rar, .7z, .tar, .gz}, programs: {.exe, .msi, .dmg, .AppImage}, scripts: {.py, .sh, .js, .go, .c, .cpp}, media: {.mp3, .mp4, .avi, .mov}, } def safe_move(src: Path, dest_dir: Path) - None: dest_dir.mkdir(parentsTrue, exist_okTrue) target dest_dir / src.name # 文件名冲突时自动加数字后缀而不是覆盖 counter 1 while target.exists(): target dest_dir / f{src.stem}_{counter}{src.suffix} counter 1 shutil.move(str(src), str(target))注意两个容易忽略的地方一是CATEGORY_MAP用set做后缀名查找因为set的成员判断是O(1)比列表的in操作快得多文件数量成百上千时差距显著二是while target.exists()循环必须设置上限否则一旦遇到极端情况比如文件名全是同一前缀会无限循环。我实际就遇到过某个目录下有几千个相似文件这层保护让脚本避免卡死。还有一点经验是默认先干跑dry-run一遍只打印“将移动哪些文件、移到哪里”不加--execute参数绝不真正移动文件。这个习惯救过我很多次尤其是对照片目录做整理时一旦移动错位置恢复成本极高。3.2 文本与日志处理提取和清洗的边界问题文本处理类脚本的典型场景是日志提取。text_processing/里的extract_log.py核心功能是从滚动日志中按时间段和关键字抽取上下文。写这类脚本最大的难点不是正则本身而是定义“一段日志”的边界。很多时候一条日志记录跨多行比如Python tracebackJava异常堆栈等只按行匹配会截断关键信息。我的做法是先判断“本行是否是一条日志的新起点”通过时间戳模式来划分记录而不是按固定行数分割import re from datetime import datetime # 匹配常见日志行首时间戳如 2025-06-01 10:23:45 TIMESTAMP_RE re.compile(r^(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})) def extract_slice(log_path, start_time, end_time, keywordNone): start_dt datetime.strptime(start_time, %Y-%m-%d %H:%M:%S) end_dt datetime.strptime(end_time, %Y-%m-%d %H:%M:%S) records [] current [] current_ts None with open(log_path, r, encodingutf-8, errorsreplace) as f: for line in f: m TIMESTAMP_RE.match(line) if m: # 新记录开始先处理上一条 if current and current_ts is not None: records.append((current_ts, current)) current_ts datetime.strptime(m.group(1), %Y-%m-%d %H:%M:%S) current [line.rstrip()] elif current is not None: # 非时间戳开头的行归属当前记录 current.append(line.rstrip()) # 按时间范围过滤 result [] for ts, lines in records: if start_dt ts end_dt: if keyword is None or any(keyword in l for l in lines): result.append(\n.join(lines)) return \n\n.join(result)实战中还有两个经验日志文件编码千奇百怪UTF-8带BOM、GBK混用非常常见。读取时用errorsreplace而不是直接报错退出这样至少能把内容提取出来坏字符后续再单独排查。超大日志不能一次性read()进内存。几GB的日志文件一次性读入内存直接卡死必须逐行迭代。上面代码就是流式处理的模式。3.3 系统巡检与资源监控类定时巡检不能只靠手动系统运维类脚本最典型的代表是system_ops/system_report.py用于收集服务器的基础信息、资源使用率、异常进程和磁盘状态汇总成可读报告。既然要定时跑输出格式就必须统一我的做法是直接生成简单的HTML或Markdown方便后续通过邮件或其他方式发送。写巡检脚本时有几个细节容易踩坑磁盘空间判断的阈值不能写死。不同分区容量差异巨大一个10GB的分区用了8GB和1TB的分区用了800GB完全不是一个风险等级。用百分比而不是绝对数值比如超过80%才报警。CPU使用率的采集需要时间窗口。读取一次/proc/stat算出来的只是瞬时值波动极大。更稳妥的做法是间隔几秒采样两次再取差值计算平均负载。这也是为什么很多监控脚本看着简单实际跑起来数据却不准的原因。脚本核心部分我保留了一个实用的进程排行逻辑#!/bin/bash # 按CPU和内存占用列出Top N进程附带完整命令行 ps aux --sort-%cpu | head -n 15 echo ---内存占用Top--- ps aux --sort-%mem | head -n 10 echo ---磁盘分区使用率--- df -h | awk $NF ! tmpfs {print}df -h的输出要看懂第一行是文件系统信息重点看“Use%”列和“Mounted on”列。过滤掉tmpfs是为了排除虚拟内存文件系统它们占用看着高但实际不影响物理磁盘。3.4 办公自动化类报表合并的高频场景办公自动化是“脚本能带来幸福感”最明显的一类。office_auto/里最有价值的是merge_excel.py它能把几十个结构相同的Excel表格合并成一个汇总表然后自动生成汇总统计。这个脚本在公司里给行政、财务、运营同学解决了不少重复劳动。实现上我避免了重型框架直接用Python标准库加最轻量的方式处理import csv import glob from collections import Counter def merge_report(input_dir./reports, outputmerged.csv): header None all_rows [] for csv_file in sorted(glob.glob(f{input_dir}/*.csv)): with open(csv_file, r, encodingutf-8-sig) as f: reader csv.reader(f) rows list(reader) if not rows: continue if header is None: header rows[0] # 跳过表头避免重复 all_rows.extend(rows[1:]) # 统计每一列的出现频次便于快速核对 if all_rows: col_counter Counter(len(r) for r in all_rows) print(行字段数分布, dict(col_counter)) with open(output, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow(header or []) writer.writerows(all_rows) print(f合并完成共 {len(all_rows)} 行数据输出至 {output})注意这里读取用的是utf-8-sig编码写入也用utf-8-sig原因在于Excel默认用带BOM的UTF-8识别加了BOM后Excel打开CSV才不会乱码。很多写CSV的脚本都栽在这个细节上数据本身没问题但用户打开看到乱码第一反应就是脚本写坏了。真实使用中总体流程比代码本身重要先把Excel另存为CSV再跑合并脚本得到汇总CSV后可以继续交给报表工具做透视。直接用Python库去操作xlsx文件当然也可以但对十几MB的大表CSV方案速度遥遥领先而且不容易遇到样式错乱的问题。3.5 网络与连通性工具一键诊断胜过到处翻命令网络排查类脚本的定位是“把常用命令封装成好记的工具”。network_utils/check_network.py一次执行能输出本机IP、默认网关、DNS解析结果、到目标主机的连通性和延迟、以及开放的常用端口。这里重点说说端口连通性检测的实现因为很多人只是会用telnet但脚本自动化时telnet并不好用import socket def check_port(host, port, timeout3): 检测TCP端口是否可连通返回(是否成功, 耗时ms) sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) start time.time() try: result sock.connect_ex((host, port)) elapsed int((time.time() - start) * 1000) return (result 0, elapsed) except socket.gaierror: return (False, -1) # DNS解析失败 except socket.timeout: return (False, timeout * 1000) finally: sock.close()重点理解connect_ex的返回值0表示连接成功非0值对应各种错误码。不要用connect()再捕获异常在批量扫描场景下connect_ex性能表现更好也更便于统一处理。timeout设3秒对多数内网环境足够但长距跨机房场景需要调大否则误报率高。诊断类脚本还有一条重要原则输出必须给人看而不是给机器看。如果能用彩色标记连通状态就把失败的项用醒目标记展示出来让人一眼找到问题。一个“跑完就是一片普通文字”的诊断脚本实际排查问题时会加重阅读负担。4. 部署、调用与调度的完整流程4.1 环境准备统一解释器版本和依赖管理100个脚本分布在多个目录必须解决好环境一致性问题。我最终做法是建立了虚拟环境并写了requirements.txt统一管理依赖。Python脚本统一用python3执行并且不允许在脚本内使用系统Python的第三方库路径全部依赖显式声明。创建环境的完整命令序列如下cd ~/scripts python3 -m venv .venv source .venv/bin/activate pip install --upgrade pip cat requirements.txt EOF requests2.32.3 openpyxl3.1.2 pandas2.2.2 PyYAML6.0.2 EOF pip install -r requirements.txt这里刻意锁了版本号而不是依赖最新版本。日常工具脚本追求稳定某天pandas升级到不兼容版本可能导致多个脚本直接崩溃。万一后续有升级需求再主动执行pip install --upgrade验证测试。4.2 统一入口让脚本像命令一样随处可调脚本整理初期我遇到最大的使用障碍是每次都要输入完整路径才能执行python ~/scripts/filesystem/organize_downloads.py长、容易敲错、还要记得路径。后来把所有脚本统一暴露给一个调度入口run_script.py原理是扫描目录结构并动态加载指定脚本通过命令行参数自动匹配。#!/usr/bin/env python3 import sys import importlib.util from pathlib import Path SCRIPTS_ROOT Path(__file__).parent def find_script(name): # 在所有分类目录中搜索名称完全匹配的脚本文件 for py in SCRIPTS_ROOT.rglob(*.py): if py.stem name: return py return None if __name__ __main__: if len(sys.argv) 2: print(用法: python run_script.py 脚本名 [参数...]) sys.exit(1) script_name sys.argv[1] script_path find_script(script_name) if script_path is None: print(f未找到脚本: {script_name}) sys.exit(1) # 把剩余参数透传给目标脚本 sys.argv [str(script_path)] sys.argv[2:] spec importlib.util.spec_from_file_location(script_name, script_path) module importlib.util.module_from_spec(spec) spec.loader.exec_module(module)有了这个入口任意路径下执行python3 ~/scripts/run_script.py organize_downloads --execute即可不用再关心脚本放在哪个分类目录。更进一步还可以把入口软链到~/bin/下加入PATH以后直接敲run-script operate_downloads就行。4.3 定时任务与日志归档自动化是脚本的最终归宿日常实用脚本的高频使用场景是定时执行。我的基本配置方式是Crontab但有三条铁律脚本内必须做日志输出不能用print看结果。因为定时任务执行时没有终端输出丢了等于白跑。好的实践是每个脚本都支持--log参数把运行记录写入固定日志文件。定时任务里必须写绝对路径。Crontab的环境变量和交互登录环境不同脚本里使用相对路径极易出错。脚本内部涉及路径的部分统一从环境变量或配置读取。给定时任务加锁防止重入。如果脚本处理时间超过定时周期上一次还没跑完下一次就开始了可能造成数据错乱。用flock命令做进程锁是最简单的方案。Crontab配置示例# 每天早上9点执行系统巡检 0 9 * * * cd /home/someone/scripts .venv/bin/python run_script.py system_report --log /var/log/scripts/system_report.log /var/log/scripts/cron.log 21 # 每小时执行一次日志备份带flock防重入 5 * * * * /usr/bin/flock -n /tmp/backup_sync.lock /home/someone/scripts/run_script.py backup_rotate --log /var/log/scripts/backup_rotate.log 21定时任务第一次跑完后务必人工检查日志内容看输出是否符合预期。很多“脚本自动跑了但没效果”的问题往往就是第一次运行时环境不对或者路径错了后续一直静默失败。5. 常见问题与排查技巧实录5.1 编码问题脚本在Windows和Linux间来回搬运时必踩的坑最常见、也最容易让新手困惑的问题就是编码不一致。同一份CSV文件在Windows上Excel另存的默认编码是GBK在Linux上常见的UTF-8。写脚本如果固定用某种编码读取必然在一个平台报错。我的排查顺序是先file 文件名看系统检测出的编码格式再决定用哪种编码打开。代码层面读取文本时加一层编码自动探测逻辑def smart_open(path): 自动猜测文件编码并读取优先UTF-8其次GBK兜底用latin-1 raw open(path, rb).read(4096) if raw.startswith(b\xef\xbb\xbf): return open(path, r, encodingutf-8-sig) try: return open(path, r, encodingutf-8) except UnicodeDecodeError: try: return open(path, r, encodinggbk) except UnicodeDecodeError: return open(path, r, encodinglatin-1)注意latin-1是最后兜底选项它能把每个字节都映射成一个字符保证文件不至于直接打不开但内容显示可能乱码。这个兜底方案至少能让你拿到原始字节数据便于进一步分析而不是让程序瞬间崩溃。5.2 路径与权限问题为什么我的脚本别人跑不了100个脚本给团队其他人用时常常出现“我本地能跑他那边就报错”。排除环境变量差异后90%的原因是脚本内部用了绝对路径或者用了当前工作目录假设。例如脚本里写了report_dir ./reports但别人在别的目录执行路径就指向了别处。规范做法是脚本默认以“脚本文件所在目录”为基准而不是以“执行命令时的当前目录”为基准import pathlib BASE_DIR pathlib.Path(__file__).resolve().parent DEFAULT_REPORT_DIR BASE_DIR / reports另一个高频坑是Shell脚本没有执行权限。每次写完Shell脚本都要记得chmod x script.sh否则即使脚本逻辑完全正确别人用./script.sh运行也会报Permission denied。如果打包给团队使用最好在文档里写清楚安装命令。5.3 性能瓶颈数据量大时脚本卡死怎么处理日常需求的数据量通常不大但偶尔会遇到大数据集比如几万条Excel记录、几十GB的日志文件。这时候脚本设计上的差别就被放大了。我踩过最深的坑是在一个合并统计脚本里直接pd.read_excel读取几十个Excel文件每个文件几万行结果内存占用冲到几个GB脚本卡死。用pandas读xlsx确实方便但Excel格式本身解压解析开销巨大。改成先批量转CSV后再用流式处理速度和内存占用都省了十倍以上。还有一个容易被忽视的性能杀手在循环体内反复打开文件或者反复连接数据库。这种问题很难从代码静态分析中看出来只能通过实际运行时间和资源监控定位。排查时用time python script.py先看总耗时再用top或pidstat看运行期间的CPU和内存曲线。如果CPU占用很低但耗时很长大概率在等待IO优先优化文件读写或网络请求。5.4 使用频率降低的信号什么时候应该删掉或重写脚本维护脚本库半年后我意识到“100个脚本”不能是一潭死水。如果一个脚本连续两三个月都没有被执行过它就在消耗你的认知资源。我的处理方式是移入archive/目录标记“已弃用”但保留历史代码。这样既不打扰日常使用又保留了解决方案的参考价值。另一个信号是“改动越来越频繁”。如果一个脚本每隔几天就要改一次参数或逻辑说明你对它的需求理解还不够准确。与其一直打补丁不如停下来重写。重写时不要把旧代码直接改两行就算完事而是回到“它到底要解决什么核心问题”想一遍往往会发现可以简化掉一半的复杂度。6. 从100到更多脚本库的长期演进6.1 沉淀模板而不是积累一次性代码脚本数量过了一百之后真正有价值的不是代码本身而是可复用的模式模板。我现在写新脚本时会直接套用固定的脚本骨架头部Docstring、参数解析、日志输出、异常处理。这个骨架本身就是从100个脚本中总结出来的。#!/usr/bin/env python3 脚本用途说明 用法示例: python3 script_name.py --input xxx --output yyy 依赖: requests import argparse import logging import sys # 统一日志配置输出到控制台可指定日志文件 def setup_logging(log_fileNone): handlers [logging.StreamHandler()] if log_file: handlers.append(logging.FileHandler(log_file, encodingutf-8)) logging.basicConfig( levellogging.INFO, format%(asctime)s %(levelname)s %(message)s, handlershandlers ) def parse_args(): parser argparse.ArgumentParser(description) parser.add_argument(--input, requiredTrue) parser.add_argument(--output, default) parser.add_argument(--log, defaultNone, help日志文件路径) return parser.parse_args() def main(): args parse_args() setup_logging(args.log) logging.info(f开始执行参数: {args}) # 核心逻辑 logging.info(执行完成) if __name__ __main__: sys.exit(main())写长得可能觉得模板本身没什么技术含量但实际价值在于统一的参数风格、统一的日志行为、统一的结构。团队里任何人接手任何一个脚本都能立刻看懂入口和数据流向省去大量的解释成本。6.2 文档与检索让脚本库“可搜索”脚本积累到一定规模检索效率变得和编写效率一样重要。我维护了一个INDEX.md按分类记录了每个脚本的名称、一句话功能、使用频率、是否推荐使用。这个文件本身也是脚本自动生成的每周运行一次update_index.py扫描脚本头部的Docstring和时间戳自动把内容同步进去。加上前文说的run_script.py统一入口完整的检索链条是这样的想找“用什么工具能批量压缩图片”grep -i 压缩\|图片\|image INDEX.md直接定位到脚本名。不记得准确名字在统一入口执行run_script.py --search 关键字自动遍历所有脚本的Docstring和文件名。确认后直接运行参数风格一致无需重新学习。这一步彻底改变了工具库的使用体验。以前是“翻文件夹找半天”现在是“一条命令找到并执行”日常使用频率提升了两倍都不止。6.3 安全执行脚本库变大后最容易忽略的事脚本库变大后安全性是我后来才意识到的问题。批量脚本经常涉及文件覆盖、移动、删除操作一旦逻辑写错轻则数据错乱重则把重要文件清理掉。我给高危操作脚本加了两道防线默认--dry-run只打印操作计划不实际执行。强制要求显式加--execute才能动手杜绝误操作。执行前自动备份受影响文件到.backup/目录备份保留30天后再自动清理。这里有个小技巧删除类操作不要直接用rm而是移到临时trash目录过几天确认没问题再清空。用Python实现也很简单shutil.move到~/.trash/即可。这套机制让脚本在外人手里也足够安全。7. 写在最后的实践心得100个脚本整理到这个程度我对“日常实用”四个字有了更具体的理解。所谓实用不是代码多漂亮、技术多前沿而是你真正会去用、能放心用、出问题敢修。建议你按照这个思路把散落在自己电脑里的脚本都捞出来先分类、再规范命名、然后补上统一入口。整理这个动作本身的价值有时比写新脚本还要大。最后分享一个我保留了很久的习惯每次解决完一个临时性问题都顺手把那个“一次性处理命令”沉淀成正式脚本。100个脚本就是这样在日常工作中一点一点长出来的。工具库不是规划出来的而是积累出来的关键是你要有一套让积累可持续的结构。