news 2026/8/24 21:06:11

Shell脚本编程实战:从零基础到自动化运维利器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Shell脚本编程实战:从零基础到自动化运维利器

如果你是一名运维工程师、开发人员,或者任何需要与 Linux 服务器打交道的人,那么你大概率经历过这样的场景:每天重复执行一堆命令,手动检查日志、备份文件、部署服务,不仅效率低下,还容易出错。你或许听说过 Shell 脚本能解决这些问题,但面对满屏的符号和命令,又觉得无从下手。

这篇文章要解决的核心问题,就是如何让一个零基础的人,真正掌握 Shell 脚本编程,并将其转化为解决实际运维和开发问题的自动化利器。我们不止讲语法,更会聚焦于“实战”:从变量、循环、函数这些基础,到被誉为“三剑客”的 grep、sed、awk 这些文本处理神器,最后串联成一个完整的自动化任务。

我的核心判断是:Shell 脚本的精髓不在于记住所有命令,而在于理解其“胶水语言”的本质——如何将简单的命令组合成强大的自动化流程。很多人学了很久,依然写不出健壮的脚本,问题往往出在忽略了错误处理、环境兼容性和安全风险上。

读完本文,你将能独立编写用于日志分析、批量文件处理、服务监控和部署的脚本,并理解编写生产级脚本时必须注意的那些“坑”。

1. Shell 脚本:为什么是运维和开发的必备技能?

在深入语法之前,我们必须先回答一个问题:在 Python、Go 等高级语言如此流行的今天,为什么还要学 Shell 脚本?

答案在于它的“不可替代性”“场景契合度”

  1. 与操作系统原生集成:Shell 是 Linux/Unix 系统的默认命令行解释器。几乎所有系统管理任务(进程管理、文件操作、权限设置)的首选接口就是 Shell 命令。用 Shell 脚本自动化这些任务,是最直接、最轻量级的方式,无需额外安装运行时环境。
  2. 强大的管道和重定向:Shell 的|(管道)、>(重定向)、<(输入重定向) 机制,使得组合多个单一功能的命令变得异常简单和高效。这种“组合哲学”是 Shell 脚本的核心优势。
  3. 启动速度快,资源消耗低:对于简单的文件操作、文本过滤、任务调度等,启动一个 Python 解释器的开销远高于执行一个 Shell 脚本。在需要频繁执行或资源受限的环境中,这一点至关重要。
  4. 无处不在的适用场景:查看热门搜索词,“日常运维中 shell 脚本适合解决哪些问题?” 这正是关键。它适合解决:
    • 批量处理:重命名大量文件(linux用shell重命名文件)、转换编码、压缩备份。
    • 日志分析:提取错误信息、统计访问量、监控特定关键词。
    • 自动化部署:拉取代码、编译、打包、上传、重启服务。
    • 系统监控:定期检查磁盘空间、内存使用率、服务状态并发送报警。
    • 数据清洗:配合grep,sed,awk快速预处理文本数据。

然而,搜索词中也暴露了另一个高频问题:“编写脚本时应注意哪些风险?” 这恰恰是很多教程忽略的部分。一个充满rm -rf且没有错误检查的脚本,其破坏力是惊人的。因此,本文在讲解能力的同时,会同等强调安全与健壮性。

2. 基础概念与核心原理:Shell 是什么?

让我们从最根本的概念开始,避免后续的混淆。

Shell:它是一个命令解释器,是用户与 Linux 内核之间的桥梁。你输入的命令(如ls,cd)由 Shell 接收、解释,然后调用内核执行。常见的 Shell 有 Bash、Zsh、Ksh。在绝大多数 Linux 发行版中,/bin/bash是默认的 Shell,也是本文的标准。

Shell 脚本:本质上是一个包含了一系列 Shell 命令的文本文件。当这个文件被设置为可执行,并由 Shell 解释执行时,它就成为了一个程序。

脚本的执行方式

  1. bash script.sh:明确指定用 Bash 解释器执行。
  2. ./script.sh:需要脚本文件具有可执行权限 (chmod +x script.sh),并且脚本第一行必须指定解释器(如#!/bin/bash)。

一个最简单的脚本: 创建一个文件hello.sh

#!/bin/bash # 这是一个注释 echo "Hello, CSDN!"

赋予执行权限并运行:

chmod +x hello.sh ./hello.sh

输出:Hello, CSDN!

#!/bin/bash这个特殊的注释称为Shebang,它告诉系统使用哪个解释器来执行此脚本。这是编写可移植脚本的第一步。

3. 环境准备与前置条件

学习 Shell 脚本,你只需要一个能运行 Shell 的环境。

  1. Linux 系统:任何发行版均可(Ubuntu, CentOS, Debian 等)。你可以使用:

    • 物理机或虚拟机安装(搜索词中虚拟机安装linux系统是常见第一步)。
    • Windows 10/11 的 WSL (Windows Subsystem for Linux)。(注意:搜索词中提到“适用于 linux 的 windows 子系统下载慢”,这是网络问题,可尝试更换源或使用离线包)。
    • Mac OS 的终端(其默认 Shell 是 Zsh,但兼容绝大部分 Bash 语法)。
  2. 一个文本编辑器:Vim, VS Code, Sublime Text 等均可。确保保存文件时使用UTF-8编码,行尾符为LF(Unix 格式),避免在 Windows 编辑后到 Linux 执行出现^M错误。

  3. 打开终端:所有练习都将在终端中进行。

验证环境: 在终端中输入以下命令,确认你的 Bash 版本。

bash --version

输出应类似:GNU bash, 版本 5.1.16(1)-release...

4. 变量:脚本的“记忆单元”

变量是存储数据的容器。Shell 变量非常灵活,但也有一些独特的规则。

4.1 变量的定义与使用

#!/bin/bash # 定义变量,等号两边不能有空格! name="Shell Learner" version=1.0 # 使用变量:需要在变量名前加美元符号 `$` echo "Welcome, $name" echo "Script version is $version" # 另一种使用方式:${variable} echo "My name is ${name}Script" # 大括号用于明确变量边界,避免混淆

关键点

  • 变量名由字母、数字、下划线组成,不能以数字开头。
  • 赋值时=两边绝对不能有空格,这是最常见的语法错误之一。
  • 使用变量时加$。对于字符串拼接,使用{}是更清晰、更安全的好习惯。

4.2 变量的类型:字符串、数字与数组

Shell 变量默认都是字符串。即使你赋值count=1,它也是字符串 “1”。但在算术上下文中,它会被解释为数字。

数组:Shell 支持一维数组。

#!/bin/bash # 定义数组 fruits=("Apple" "Banana" "Orange") # 访问数组元素,下标从0开始 echo "First fruit: ${fruits[0]}" # 输出:Apple # 访问所有元素 echo "All fruits: ${fruits[@]}" # 获取数组长度 echo "Number of fruits: ${#fruits[@]}" # 输出:3 # 遍历数组 for fruit in "${fruits[@]}"; do echo "I like $fruit" done

数组在处理一组相关数据时非常有用,比如需要批量处理的文件名列表。

4.3 环境变量与特殊变量

  • 环境变量:对整个 Shell 会话和其子进程都可见的变量。如PATH,HOME,USER
    echo "My home directory is $HOME" echo "Current user is $USER" # 自定义环境变量(仅对当前会话有效) export MY_VAR="some_value"
  • 特殊变量:Shell 预定义的,用于获取脚本参数和状态。
    # 假设脚本执行方式为:./script.sh arg1 arg2 echo "Script name: $0" # 脚本名 ./script.sh echo "First argument: $1" # arg1 echo "Second argument: $2" # arg2 echo "All arguments: $@" # arg1 arg2 (每个参数独立) echo "All arguments as one: $*" # “arg1 arg2” (一个整体) echo "Number of arguments: $#" # 2 echo "Last command exit status: $?" # 上一条命令的退出状态,0表示成功 echo "Current process ID: $$" # 脚本运行的进程ID
    $?在错误处理中至关重要。$@在遍历所有脚本参数时比$*更安全。

5. 流程控制:让脚本做出判断和重复劳动

5.1 条件判断 (if/else)

Shell 使用test命令或其等价符号[ ]/[[ ]]进行条件判断。

#!/bin/bash read -p "Enter a number: " num # 使用 [ ] 注意空格!括号内两端必须有空格,变量最好用双引号引起来 if [ "$num" -gt 10 ]; then echo "$num is greater than 10" elif [ "$num" -eq 10 ]; then echo "$num is equal to 10" else echo "$num is less than 10" fi # 更现代的 [[ ]],支持更强大的匹配(如正则),且变量可以不用引号(但仍建议使用) if [[ $num =~ ^[0-9]+$ ]]; then echo "You entered a valid number." else echo "Invalid input!" fi # 文件判断 file="/etc/passwd" if [ -f "$file" ]; then # 判断是否为普通文件 echo "$file exists and is a regular file." fi if [ -d "/tmp" ]; then # 判断是否为目录 echo "/tmp is a directory." fi

常见判断运算符

  • 数值比较-eq(等于),-ne(不等于),-gt(大于),-lt(小于),-ge(大于等于),-le(小于等于)。
  • 字符串比较===(相等),!=(不相等),-z(字符串长度为0),-n(字符串长度非0)。
  • 文件测试-e(存在),-f(是普通文件),-d(是目录),-r(可读),-w(可写),-x(可执行)。

5.2 循环:自动化重复任务

搜索词中shell脚本for循环while循环是绝对的高频需求。

for 循环:常用于遍历列表。

#!/bin/bash # 遍历数字序列 for i in {1..5}; do echo "Iteration $i" done # 遍历命令输出结果(例如遍历当前目录下的 .txt 文件) for file in *.txt; do echo "Processing $file" # 可以在这里对每个文件进行操作,比如 wc -l "$file" done # 经典的 C 语言风格 for 循环 for ((i=0; i<5; i++)); do echo "C-style loop: $i" done

while 循环:当条件为真时持续执行。

#!/bin/bash # 读取文件每一行 while IFS= read -r line; do # IFS= 和 -r 是为了正确处理行内空格和反斜杠 echo "Line: $line" done < /etc/hosts # 输入重定向,将文件内容喂给 while 循环 # 计数器循环 count=1 while [ $count -le 5 ]; do echo "Count is $count" ((count++)) # 使用双括号进行算术运算 done # 无限循环(需要内部有 break 条件) while true; do echo "Press [CTRL+C] to stop.." sleep 1 done

until 循环:与while相反,条件为假时执行。

until [ $count -gt 5 ]; do echo "Count: $count" ((count++)) done

6. 函数:封装可重用的代码块

函数让脚本模块化,避免代码重复。

#!/bin/bash # 函数定义 function greet() { local name=$1 # local 关键字声明局部变量,避免污染全局 local hour=$(date +%H) if [ $hour -lt 12 ]; then echo "Good morning, $name!" else echo "Hello, $name!" fi return 0 # 返回值(0-255),通常0表示成功 } # 另一种定义方式 log_message() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> /tmp/myscript.log } # 函数调用 greet "Alice" greet "Bob" log_message "Script started." # 获取函数返回值(通过 $? 获取的是 return 的值) # 获取函数输出(通过命令替换 `$()`) current_greeting=$(greet "Charlie") echo "The greeting was: $current_greeting"

函数最佳实践

  1. 使用local定义函数内变量。
  2. 函数名应清晰描述其功能。
  3. 通过return返回状态码,通过echo或全局变量返回数据。
  4. 在函数开头进行参数校验。

7. 文本处理“三剑客”:grep, sed, awk

这是 Shell 脚本编程中威力最强大的部分,也是区分新手和老手的关键。它们各自擅长不同的文本处理领域。

7.1 grep:全局正则表达式打印,用于搜索

grep在文件或标准输入中搜索匹配模式的行。

# 在文件 /var/log/syslog 中查找包含 “error” 的行(忽略大小写) grep -i "error" /var/log/syslog # 递归搜索当前目录下所有 .java 文件中的 “public class” grep -r "public class" . --include="*.java" # 显示匹配行及其后2行(-A: After, -B: Before, -C: Context) grep -A2 -B2 "panic" /var/log/kern.log # 只显示匹配到的部分(-o),并结合正则表达式提取IP地址 echo "Server IP is 192.168.1.1 and client is 10.0.0.2" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' # 输出: # 192.168.1.1 # 10.0.0.2

7.2 sed:流编辑器,用于文本替换和编辑

sed以行为单位,对文本进行替换、删除、新增、选取等操作。

# 将文件 input.txt 中所有的 “foo” 替换为 “bar”,并输出到屏幕 sed 's/foo/bar/g' input.txt # ‘s’ 表示替换,‘g’ 表示全局(一行中所有匹配项) # 直接修改原文件(-i 选项,生产环境务必先备份!) sed -i.bak 's/old/new/g' file.txt # 先备份为 file.txt.bak,再修改 file.txt # 删除包含 “debug” 的行 sed '/debug/d' app.log # 在文件第3行后插入一行文本 sed -i '3a\This is a new line' file.txt # ‘a’ 表示追加(after),‘i’ 表示插入(before) # 使用扩展正则表达式(-E),匹配更复杂的模式 echo "abc123def" | sed -E 's/[a-z]+([0-9]+).*/\1/' # 输出:123 (提取数字部分)

7.3 awk:强大的文本分析工具,更像一门编程语言

awk将文件逐行读入,以空格(默认)为分隔符将每行切片,然后对切片进行处理。

# 打印 /etc/passwd 文件的第一列(用户名)和第三列(用户ID) awk -F: '{print $1, $3}' /etc/passwd # -F: 指定冒号为字段分隔符 # $1, $3 分别代表第一和第三个字段 # 计算文件的总行数(NR 是内置变量,表示已读的记录数) awk 'END {print NR}' access.log # 对数据进行过滤和计算:统计状态码为 200 的请求数量 awk '$9 == 200 {count++} END {print “200 OK count:”, count}' access.log # 假设日志格式中第9个字段是状态码 # 更复杂的例子:计算每个用户的进程占用内存总和(ps 输出) ps aux | awk 'NR>1 {user[$1] += $4} END {for (u in user) print u, user[u] “%”}' # $1 是用户名,$4 是 %MEM。NR>1 跳过标题行。

“三剑客”组合使用示例:找出访问日志中访问量最高的前5个IP。

awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -5 # 1. awk 提取第一列(IP) # 2. sort 排序,使相同IP相邻 # 3. uniq -c 统计并计数 # 4. sort -rn 按计数数字反向排序(从大到小) # 5. head -5 取前5行

8. 实战:编写一个完整的日志分析监控脚本

现在,我们将前面所有知识融合,编写一个实用的脚本。这个脚本将:

  1. 监控一个日志文件(如 Nginx 的 access.log)。
  2. 实时统计 HTTP 状态码的分布。
  3. 检测到异常状态码(如 5xx)超过阈值时发送告警(模拟)。
  4. 定期归档旧的日志文件。

脚本文件:log_monitor.sh

#!/bin/bash # 日志监控与分析脚本 # 用法:./log_monitor.sh /path/to/access.log set -euo pipefail # 安全模式:遇到错误退出,使用未定义变量报错,管道中任意命令失败则整个管道失败 # === 配置部分 === LOG_FILE="${1:-/var/log/nginx/access.log}" # 使用第一个脚本参数,默认为nginx日志 ALERT_THRESHOLD=10 # 5xx错误告警阈值(次数) ARCHIVE_DIR="./log_archive" # 日志归档目录 REPORT_FILE="./status_report_$(date +%Y%m%d).txt" # 日报文件 # === 函数定义 === # 发送告警(模拟,实际可替换为邮件、钉钉、企业微信等) send_alert() { local message="$1" echo "[ALERT] $(date '+%Y-%m-%d %H:%M:%S') - $message" | tee -a "$REPORT_FILE" >&2 # 这里可以集成真实的告警接口,例如: # curl -X POST -H "Content-Type: application/json" -d "{\"msg\":\"$message\"}" $WEBHOOK_URL } # 分析日志状态码 analyze_status_codes() { local log_file="$1" echo "=== HTTP 状态码分析 ($(date)) ===" >> "$REPORT_FILE" # 使用 awk 统计 awk '{print $9}' "$log_file" | sort | uniq -c | sort -rn | while read count code; do echo "状态码 $code: $count 次" | tee -a "$REPORT_FILE" # 检查5xx错误 if [[ $code =~ ^5[0-9]{2}$ ]] && [ $count -gt $ALERT_THRESHOLD ]; then send_alert "检测到过多服务端错误!状态码: $code, 出现次数: $count" fi done echo "--------------------------------" >> "$REPORT_FILE" } # 归档日志(按日期) archive_old_logs() { local source_dir=$(dirname "$LOG_FILE") local log_name=$(basename "$LOG_FILE") find "$source_dir" -name "${log_name}.*" -mtime +7 -type f | while read old_log; do if [ ! -d "$ARCHIVE_DIR" ]; then mkdir -p "$ARCHIVE_DIR" fi echo "归档旧日志: $old_log" gzip -c "$old_log" > "${ARCHIVE_DIR}/$(basename "$old_log").$(date +%Y%m%d).gz" # 生产环境谨慎删除!这里先注释掉 # rm "$old_log" done } # === 主程序 === main() { echo "开始监控日志文件: $LOG_FILE" # 1. 检查日志文件是否存在 if [ ! -f "$LOG_FILE" ]; then echo "错误:日志文件 $LOG_FILE 不存在!" >&2 exit 1 fi # 2. 分析状态码 analyze_status_codes "$LOG_FILE" # 3. 检查日志文件大小,如果过大则提醒轮转(模拟) local log_size=$(du -m "$LOG_FILE" | cut -f1) if [ $log_size -gt 100 ]; then # 假设超过100MB需要轮转 send_alert "日志文件 ${LOG_FILE} 大小超过100MB,建议进行日志轮转。" fi # 4. 归档旧日志(可以放在cron job里定期执行,这里演示) archive_old_logs echo "分析完成。报告已保存至: $REPORT_FILE" } # 执行主函数 main

运行与验证

  1. 保存脚本,赋予执行权限:chmod +x log_monitor.sh
  2. 准备一个模拟的访问日志文件,或使用你真实的 Nginx 日志。
  3. 运行脚本:./log_monitor.sh /path/to/your/access.log
  4. 查看生成的报告文件status_report_YYYYMMDD.txt

这个脚本涵盖了变量、函数、条件判断、文件测试、命令替换、管道、以及“三剑客”中的awkfind。它是一个功能完整、结构清晰的模板,你可以根据实际需求修改和扩展。

9. 常见问题与排查思路

在编写和运行 Shell 脚本时,你一定会遇到各种错误。下表列出了最常见的问题及其解决方法。

问题现象可能原因排查方式解决方案
bash: ./script.sh: Permission denied脚本文件没有可执行权限。ls -l script.shchmod +x script.sh
bash: ./script.sh: /bin/bash^M: bad interpreter脚本是在 Windows 下编辑的,行尾是 CRLF (\r\n)。cat -A script.sh查看行尾是否有^M使用dos2unix script.sh转换,或在编辑器中设置为 Unix 行尾 (LF)。
[: too many arguments[: =: unary operator expected条件判断[ ]中的变量未加引号,且变量值为空或包含空格。检查if [ $var = “value” ],如果$var为空,则变成[ = “value” ]永远将变量用双引号引起来if [ “$var” = “value” ]。或使用[[ ]]
command not found1. 命令拼写错误。
2. 命令未安装。
3. 命令路径不在$PATH中。
1. 检查拼写。
2. 用which commandtype command查看。
3.echo $PATH查看路径。
1. 纠正拼写。
2. 安装对应软件包。
3. 使用绝对路径或修改PATH
脚本执行成功,但结果不对或文件没变化。1. 逻辑错误(如条件判断符号用错)。
2. 使用了-i选项的sedrm等命令,但脚本没有实际权限。
3. 路径错误,操作了错误文件。
1. 使用set -x在脚本开头启用调试,查看每一步执行情况。
2. 在危险命令前加echo预览将要执行的命令。
1. 仔细检查逻辑,特别是字符串和数字比较。
2. 使用sudo或检查文件权限。
3. 使用绝对路径或在操作前用pwdls确认位置。
循环或读取文件时,行为异常(如跳过空行、分词错误)。未正确设置IFS(内部字段分隔符),或未使用-r参数读取行。默认IFS包含空格、制表符、换行符,会影响分词。while read循环前设置IFS=并加上-rwhile IFS= read -r line; do ... done < file
变量值意外被修改。变量作用域问题,在函数内未使用local声明变量,污染了全局变量。在函数内对所有变量使用local声明。养成在函数内使用local var_name的好习惯。
脚本在后台运行 (&) 或通过 cron 调度时,环境变量丢失。非交互式 Shell 加载的环境变量与登录 Shell 不同。在脚本中显式source所需的环境配置文件,或使用绝对路径。1. 在脚本开头设置关键环境变量,如PATH
2. 在 cron job 中,使用完整路径,或先source ~/.bashrc

10. 最佳实践与工程建议

要让你的 Shell 脚本从“能用”变成“可靠”、“可维护”,请遵循以下原则:

  1. 脚本开头使用set -euo pipefail

    • -e:任何命令失败(返回非零状态)立即退出脚本。
    • -u:遇到未定义的变量时报错并退出。
    • -o pipefail:管道中任何一个命令失败,整个管道就失败。 这能避免很多隐藏的错误。对于某些你期望失败的命令,可以用command || true来忽略。
  2. 总是为变量加双引号“$var”。这是防止单词拆分和路径名扩展导致错误的最简单有效的方法。

  3. 使用[[ ]]进行条件测试:它比[ ]更强大,更安全(在变量未加引号时表现更好),并且支持正则匹配。

  4. 函数化:将重复的代码块或独立的功能封装成函数。函数名要清晰,使用snake_case命名法。

  5. 添加详细的日志和错误处理:脚本不应沉默地失败。使用echo输出关键步骤信息,重定向到日志文件。对于可能失败的操作,使用if判断或trap命令捕获信号。

  6. 进行输入验证:对于用户输入或脚本参数,必须进行验证(是否为空、格式是否正确、文件是否存在等)。

  7. 谨慎处理文件名和路径:文件名可能包含空格、换行符等特殊字符。使用“$file”来引用变量,并在for循环中优先使用find -print0 | xargs -0while IFS= read -r -d ''来处理。

  8. 避免使用cd:在脚本中频繁改变目录容易导致混乱和错误。如果必须,使用子shell(cd /some/dir && command)或总是通过pushd/popd返回。

  9. 为脚本添加帮助信息 (-hor--help)和使用说明。

  10. 版本控制和代码审查:即使是 Shell 脚本,也应纳入 Git 等版本控制系统,并进行同行审查。

11. 总结与后续学习方向

通过本文,你走完了从零认识 Shell 脚本到编写一个完整自动化监控脚本的全程。我们强调了“胶水语言”的思维模式:用简单的命令组合解决复杂问题

核心要点回顾:

  • 变量是脚本的记忆,要注意定义、引用和作用域。
  • 流程控制(条件与循环)赋予脚本逻辑判断和重复执行的能力。
  • 函数是代码复用的基石,让脚本结构清晰。
  • 文本处理三剑客是 Shell 脚本的灵魂,grep查找、sed编辑、awk分析报告,三者结合几乎可以处理任何文本数据。
  • 安全与健壮性是生产脚本的生命线,从set -euo pipefail到输入验证,每一步都不可或缺。

你的下一步

  1. 动手实践:将你日常工作中重复的 3-5 个手动操作,尝试用 Shell 脚本自动化。从最简单的开始,比如批量压缩图片、清理临时文件。
  2. 深入“三剑客”:找一本专门讲解sed & awk的书,或在线教程,深入学习正则表达式和awk编程。
  3. 学习高级主题:进程管理、信号处理 (trap)、并行执行 (&,wait,xargs -P)、Here Document、调试技巧 (set -x)。
  4. 阅读优秀代码:查看 Linux 系统自带的/etc/init.d/下的服务管理脚本,或大型开源项目的构建脚本(如 Dockerfile 的 entrypoint),学习其中的模式和技巧。

Shell 脚本是 Linux 世界里的瑞士军刀,看似简单,但功力深浅全在组合与细节之中。希望这篇文章能成为你自动化之旅的坚实起点。建议收藏本文,在编写下一个脚本时,不妨回头看看“常见问题”和“最佳实践”部分,或许能帮你避开许多坑。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/24 21:04:37

三步导出微信聊天记录,免费生成年度聊天报告:WeChatMsg 使用教程

三步导出微信聊天记录&#xff0c;免费生成年度聊天报告&#xff1a;WeChatMsg 使用教程 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/Git…

作者头像 李华
网站建设 2026/8/24 21:04:16

二分答案算法精讲:从河中跳房子问题理解最值优化

1. 从“河中跳房子”到二分答案&#xff1a;一个经典思维的诞生如果你刚开始接触算法竞赛或者刷题&#xff0c;看到“1247&#xff1a;河中跳房子”这个标题&#xff0c;可能会觉得有点摸不着头脑。这听起来像是个游戏或者物理题&#xff0c;怎么就成了经典的算法例题&#xff…

作者头像 李华
网站建设 2026/8/24 20:59:35

基于SpringBoot+vue的相机租赁管理系统毕业设计项目源码

联系博主 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 …

作者头像 李华
网站建设 2026/8/24 20:59:20

分布式锁核心原理与Redisson生产级实现详解

分布式锁&#xff0c;这个在面试中高频出现、在项目中却又常常被误解或误用的技术&#xff0c;到底解决了什么问题&#xff1f;为什么单体应用时代我们很少关心它&#xff0c;而一旦系统走向分布式&#xff0c;它就变成了一个绕不开的坎&#xff1f; 很多开发者对分布式锁的理…

作者头像 李华
网站建设 2026/8/24 20:58:22

基于物理光学模型的视频眼镜移除技术:原理、部署与工程实践

这次我们来看一个名为“Unwarping the Lens: A Physics-Grounded Approach to Video Glasses Removal”的研究项目。简单来说&#xff0c;这是一个利用物理基础模型从视频中移除眼镜的技术。它要解决的核心问题是&#xff1a;当一个人戴着眼镜出现在视频中时&#xff0c;眼镜镜…

作者头像 李华