*题目一**:
在一个 Web 服务器的日志目录(假设为/var/log/httpd/)下,编写一个脚本,使用 for 循环遍历最近一周内的日志文件(按日期命名),查找其中包含状态码为 500 的请求行,如果找到,则将该行信息以及对应的日志文件名记录到一个专门的错误报告文件中(比如/var/tmp/http_errors.txt)。
思路:
核心目标:
快速定位并汇总最近一周内服务器返回 500 错误的请求记录,便于运维人员分析问题。
实现步骤拆解:
-
确定时间范围
-
使用
date -d "1 week ago"计算起始日期 -
循环生成从今天倒推 7 天的日期列表
-
-
文件路径管理
-
错误报告输出路径:
/var/tmp/http_errors.txt -
日志文件命名规则:
/var/log/httpd/YYYY-MM-DD.log
-
-
错误检测策略
-
通过
grep " 500 "精确匹配 HTTP 状态码 -
使用
-H参数强制显示文件名便于定位 -
通过
sed添加时间戳增强可读性
-
-
执行流程控制
┌───────────────────────────────────┐ │ 初始化报告文件(清空/创建) │ └─────────────────┬─────────────────┘ │ ┌───────────┴───────────┐ ▼ ▼ 循环7次(今天→6天前) 检查日志文件存在 │ │ ▼ ▼ 生成日期字符串 ────► 存在 ────► 提取500错误 │ ▼ 追加到报告文件 1. **初始化报告文件**:首先进行报告文件的初始化操作,即清空或创建位于`/var/tmp/http_errors.txt`的错误报告文件。 2. **循环日期范围**:通过循环 7 次,从今天开始倒推到 6 天前,每次循环生成一个日期字符串。例如在 2025 年 6 月 5 日运行脚本,就会生成从 6 月 5 日到 6 月 - 1 日(即 5 月 31 日)的日期。 3. **检查日志文件存在**:针对每次循环生成的日期,根据日志文件命名规则`/var/log/httpd/YYYY - MM - DD.log`,检查对应日期的日志文件是否存在。比如对于 6 月 5 日,就检查`/var/log/httpd/2025 - 06 - 05.log`文件是否存在。 4. **提取 500 错误**:若日志文件存在,使用`grep`命令在该日志文件中查找包含 “500” 的行,提取出 HTTP 500 错误相关信息。 5. **追加到报告文件**:将提取出的 500 错误信息,添加到初始化的错误报告文件`/var/tmp/http_errors.txt`中。
关键技术选择
-
日期计算:采用
date -d参数实现相对日期计算 -
日志匹配:使用
grep的精确字符串匹配避免误判 -
文件处理:通过
>和>>操作符实现文件管理 -
错误处理:自动跳过不存在的日志文件保证脚本健壮性
可扩展方向
-
增加对其他错误码的支持(如 404、403 等)
-
添加错误统计功能(按 URL/IP 分组统计)
-
集成邮件发送功能自动分发报告
-
生成 HTML 格式报告增强可读性
#!/bin/bash
# 定义错误报告文件路径
error_report_file="/var/tmp/http_errors.txt"
(此路径就是用于存储错误报告的文件路径。在后续脚本执行过程中,收集到的 HTTP 服务器错误日志信息会被保存到这个指定路径的文件里。例如在这个脚本里,后续会将提取出的包含 “500” 状态码的日志行追加到/var/tmp/http_errors.txt文件中。)
# 获取一周前的日期
week_ago=$(date -d "1 week ago" +%Y-%m-%d)
(整体意思是获取当前日期往前推一周的日期,并将其赋值给变量week_ago 。例如,若今天是 2025 年 6 月 5 日,那么week_ago的值会是 2025-05-29 。)
# 遍历日志文件
for log_file in /var/log/httpd/$(date +%Y-%m-%d).log /var/log/httpd/$(date -d "1 day ago" +%Y-%m-%d).log /var/log/httpd/$(date -d "2 days ago" +%Y-%m-%d).log /var/log/httpd/$(date -d "3 days ago" +%Y-%m-%d).log /var/log/httpd/$(date -d "4 days ago" +%Y-%m-%d).log /var/log/httpd/$(date -d "5 days ago" +%Y-%m-%d).log /var/log/httpd/$(date -d "6 days ago" +%Y-%m-%d).log; do
if [ -f "$log_file" ]; then
while IFS= read -r line; do
if [[ $line == *" 500 " ]]; then
echo "Error in $log_file: $line" >> "$error_report_file"
fi
done < "$log_file"
fi
done
if [[ $line == *" 500 " ]]; then:这是一个条件判断语句。$line表示从日志文件中逐行读取的内容,==是字符串比较操作符,*" 500 "*表示匹配任何包含字符串" 500 "(前后有空格)的行。如果$line包含" 500 ",则条件成立。
echo "Error in $log_file: $line" >> "$error_report_file":如果上述条件成立,执行此命令。echo用于输出内容,这里输出的是"Error in ",接着是当前处理的日志文件名$log_file,冒号后是包含" 500 "的具体日志行$line。>>表示将输出内容追加到文件中,$error_report_file是错误报告文件的路径变量,即把匹配到的错误日志信息追加到错误报告文件中。
fi:表示if条件判断的结束。
done < "$log_file":done表示while循环的结束,< "$log_file"表示从$log_file这个文件中逐行读取内容,作为while循环的输入。
优化后:
#!/bin/bash
# 定义错误报告文件路径
error_report_file="/var/tmp/http_errors.txt"
# 获取一周前的日期
week_ago=$(date -d "1 week ago" +%Y-%m-%d)
# 清空或创建错误报告文件
> "$error_report_file"
# 生成最近7天的日期列表
for i in {0..6}; do
date_str=$(date -d "$i days ago" +%Y-%m-%d)
log_file="/var/log/httpd/${date_str}.log"
# 检查文件是否存在
if [ -f "$log_file" ]; then
echo "Checking $log_file..."
# 使用grep查找包含" 500 "的行,并添加到报告
grep -H " 500 " "$log_file" | sed "s/^/${date_str}: /" >> "$error_report_file"
fi
done
echo "Error checking completed. Report saved to $error_report_file"
解析:
(循环设定:for i in {0..6}; do 表示从 0 到 6 进行循环,i 作为循环变量,依次取值 0 到 6。这意味着该循环会执行 7 次。
日期计算:date_str=$(date -d "$i days ago" +%Y-%m-%d) 这行代码使用 date 命令计算日期。其中 -d "$i days ago" 表示从当前日期往前推 i 天,+%Y-%m-%d 则是指定输出日期的格式为年 - 月 - 日,例如 “2025-06-05”。每次循环,date_str 会得到不同的日期字符串。
日志文件路径构建:log_file="/var/log/httpd/${date_str}.log" 根据计算出的日期字符串 date_str,构建对应的日志文件路径。假设 date_str 为 “2025-06-05”,则 log_file 的值为 “/var/log/httpd/2025-06-05.log” ,这是服务器上存储特定日期 HTTP 日志的文件路径。在整个脚本中,后续会根据这个路径去检查和处理对应的日志文件。)
if [ -f "$log_file" ]; then 这一行使用 if 条件判断语句和 [ -f 文件路径 ] 测试表达式,-f 用于判断给定的路径是否为一个存在的普通文件。这里的 $log_file 是之前定义的日志文件路径变量,将其代入测试表达式,判断该日志文件是否存在。如果存在,条件为真,就会执行 then 之后的命令。
echo "Checking $log_file..." 这一行是在日志文件存在时,使用 echo 命令输出提示信息,其中 $log_file 会被替换为实际的日志文件路径,方便用户了解脚本正在处理哪个日志文件。例如,如果 $log_file 的值为 /var/log/httpd/2025-06-05.log,那么就会输出 “Checking /var/log/httpd/2025-06-05.log...”
Checking $log_file.. 末尾的 两个点(..) 是手动添加的省略号,通常用于表示 “检查正在进行中” 或 “操作未完成” 的语义,属于增强用户体验的文本修饰,无特殊语法含义。
grep -H " 500 " "$log_file":使用grep命令在$log_file(某个 HTTPD 日志文件)中搜索包含字符串 "500" 的行,-H选项表示在输出结果中包含文件名。
| sed "s/^/${date_str}: /":通过管道|将grep的输出传递给sed命令,sed在这里执行替换操作,将每行的开头(^)替换为$date_str(应该是一个表示日期时间的变量)加上冒号和空格。
>> "$error_report_file":将经过sed处理后的内容追加(>>)到$error_report_file文件中,该文件用于保存包含 500 状态码的错误日志记录。
fi:结束之前if语句的条件判断块,if语句应该是在检查$log_file文件是否存在等条件。
done:结束之前的for循环,for循环应该是在遍历多个日志文件。
echo "Error checking completed. Report saved to $error_report_file":输出一条信息,提示错误检查已完成,并且报告已保存到$error_report_file文件。
题目二
自动备份文件或目录:
#!/bin/bash
# 设置备份目录
backup_dir="/path/to/backup/dir"
# 设置要备份的文件或目录
files_to_backup="/path/to/files /path/to/dir"
# 创建一个日期时间戳
timestamp=$(date +%F_%T)
# 备份文件
tar -czvf "${backup_dir}/backup_${timestamp}.tar.gz" ${files_to_backup}
解析步骤:
准备工作:
1、要有备份路径
2、要备份什么文件
3、备份时间
实施:
1、备份文件命令
tar -czvf backup.tar.gz /data # gzip 压缩(速度快)
tar -cjvf backup.tar.bz2 /data # bzip2 压缩(体积小)
(仅备份变化的文件:
tar -czvf backup.tar.gz --newer="2025-06-09" /home # 备份 2025 年后修改的文件)
#!/bin/bash
#设置备份文件目录
back_dir=/var/log/httpd
#设置要备份的文件或目录
back_to_dir="/home"
#创建时间戳
timestamp=$(date +%F_%T)
#备份文件
if tar -czf "${back_dir}/back_${timestamp}.tar.gz" ${back_to_dir}; then
echo "文件已备份到${back_dir}"
else
echo "备份失败"
exit 1
fi
题目三
批量重命名文件
#!/bin/bash
# 设置文件扩展名
extension=".txt"
# 遍历当前目录下的所有文件
for file in *${extension}
do
# 获取文件名(不包括扩展名)
filename=$(basename "${file}" "${extension}")
# 重命名文件
mv "${file}" "${filename}_new${extension}"
done
分析:
1.设置文件扩展名
extension=".txt"
这行代码定义了一个变量extension,值为.txt
这样设计的好处是,如果后续需要修改为处理其他扩展名的文件,只需修改这一行
2. 遍历当前目录下的所有文件
for file in *${extension}
do
# 循环体
done
*${extension} 是一个通配符表达式,会匹配当前目录下所有以.txt结尾的文件
例如,如果当前目录有file1.txt、file2.txt,这个表达式会扩展为这两个文件名
注意,这里没有用双引号包围*${extension},这是有意的,因为我们需要 shell 进行通配符扩展
如果使用了双引号,如"*${extension}",则会被当作字面字符串,无法进行文件匹配
3. 获取文件名(不包括扩展名,即去扩展名)
filename=$(basename "${file}" "${extension}")
basename是一个 shell 内置命令,用于从路径中提取文件名部分
第一个参数"${file}"是要处理的文件路径
第二个参数"${extension}"是要从文件名中去除的后缀
例如:
如果file的值是test.txt,则basename会返回test
如果file的值是/path/to/file.txt,则basename同样会返回file
使用双引号包围变量是为了防止文件名中包含空格等特殊字符时出现问题
4. 重命名文件
mv "${file}" "${filename}_new${extension}"
源文件是"${file}",即当前遍历到的文件
目标文件是"${filename}_new${extension}",即原文件名加上 "_new" 后缀,再加上原扩展名
例如:
如果file是test.txt,则filename是test,新文件名就是test_new.txt
如果file是data 2023.txt(包含空格),新文件名会是data 2023_new.txt
关键点分析
变量引用时的双引号
在脚本中,变量引用几乎都使用了双引号,如"${file}"、"${filename}"
这是为了防止文件名中包含空格、特殊字符等情况导致命令解析错误
例如,如果有一个文件名为my data.txt:
不加双引号:mv my data.txt my data_new.txt 会被解析为移动 4 个参数,导致错误
加双引号:mv "my data.txt" "my data_new.txt" 会被正确解析为移动一个文件
通配符扩展原理
*${extension} 是 shell 的通配符扩展机制
* 匹配任意数量(包括零个)的任意字符
${extension} 是变量替换,会被替换为.txt(大括号是划定边界作用)
因此,*${extension} 会匹配当前目录下所有以.txt结尾的文件
注意,通配符扩展是在 shell 执行命令之前完成的
例如,在执行for循环之前,*${extension}就已经被扩展为实际的文件列表了
题目四
批量删除文件
#!/bin/bash
# 设置文件名
filename="example.txt"
# 查找并删除文件
find . -name "${filename}" -delete
步骤分析:
1、查找指定文件并取反:find
2、删除除指定名称的文件:rm
#!/bin/bash
filename="1.txt"
# 查找除指定文件外的所有文件
file=$(find . -type f ! -name "${filename}")
# 删除文件
if rm ${file}; then
echo "除 ${filename} 其余文件全部删除"
else
echo "除 ${filename} 其余文件删除失败"
fi
分析:
加引号的问题
若使用 rm "${file}",相当于执行:
rm "./a.txt\n./b.txt\n./subdir/c.txt"
此时 rm 会尝试删除一个名为 ./a.txt\n./b.txt\n./subdir/c.txt 的文件(实际不存在),导致错误:
!它的位置必须严格遵循 find 的语法规则,find [路径] [条件表达式] [操作]
条件表达式:由多个条件组合而成,支持逻辑运算符(! 非、-a 与、-o 或)。
逻辑运算符的位置规则:
! 必须紧跟在条件之前,中间不能有空格。
-a/-o 通常用于连接两个条件,位于条件之间。
题目五
查找并替换文件内容
#!/bin/bash
# 设置要查找的字符串
search_string="old_string"
# 设置要替换成的字符串
replace_string="new_string"
# 查找并替换文件内容
find . -type f -exec sed -i "s/${search_string}/${replace_string}/g" {} \;
分析步骤:
标准搭配格式:
find [搜索路径] [查找条件] -exec sed [sed选项] "s/查找模式/替换内容/修饰符" {} \;(注:-exec要求;结尾且需转义)
1.find 命令启动
.:从当前目录开始搜索
-type f:只匹配文件(排除目录)
2. 执行动作 (-exec)
-exec 告诉 find 对每个匹配到的文件执行后续命令
命令格式:命令 {} \;
{} 是 find 的占位符,表示当前处理的文件名,用于动态引用当前处理的文件名。
当 find 搜索到一个文件时,会将该文件的完整路径(如 ./file.txt 或 ./subdir/doc.md)替换到 {} 的位置,然后执行后续命令。
\; 是命令结束标记,必须转义为 \;
3. sed 替换命令
-i:直接修改文件内容(in-place editing)
s/${search_string}/${replace_string}/g:
s/old/new/ 是 sed 的替换命令格式
g 标志表示全局替换(替换每行中所有匹配项)
${search_string} 和 ${replace_string} 是 shell 变量,会被实际值替换
s:表示执行替换(substitute)。
第一个 /:标记「查找模式」的开始和结束。
第二个 /:标记「替换内容」的开始和结束。
-exec结尾规则
使用反斜杠转义:\;
使用单引号包裹:';'
#!/bin/bash
#定义要替换内容的文件
filename="1.txt"
# 设置要查找的字符串
search_string="old_string"
# 设置要替换成的字符串
replace_string="new_string"
# 查找并替换文件内容
if find . -type f -name "${filename}" -exec sed -i "s/${search_string}/${replace_string}/g" {} \;
then
echo "内容替换完成"
else
echo "替换错误"
fi
1万+

被折叠的 条评论
为什么被折叠?



