Shell脚本匹配replace.txt删除.htaccess中RewriteRule的sed正则问题
.htaccess指定RewriteRule行批量删除脚本问题修复
问题背景
在清理大型.htaccess文件的冗余RewriteRule规则时,原有脚本设计逻辑为:逐行遍历.htaccess内容,匹配replace.txt中存储的待删除规则,命中则删除对应行。测试阶段已将正式.htaccess复制为test.txt作为测试对象,但脚本执行时sed正则匹配报错,无法正确处理RewriteRule行中包含的特殊字符,多次调整正则写法均未解决问题。
原有脚本代码如下:
real_htaccess_file=$(cat $file_path/.htaccess) test_htaccess_file=$(cat $file_path/test.txt) replace_rules_file=$(cat $file_path/replace.txt) # 遍历test_htaccess文件的每一行 while read -r line; do # 判断当前行是否包含RewriteRule if [[ $line == *"RewriteRule"* ]]; then # 遍历replace_rules_file的每一行 while read -r replace_line; do # 判断.htaccess行是否和待删除规则匹配 if [[ $line == $replace_line ]]; then # 打印命中的行日志 echo -e 'Found RewriteRule: '$line # 从test_htaccess_file中删除该行 sed -i "/$line/d" $file_path/test.txt fi done < $file_path/replace.txt fi done < $file_path/test.txt
脚本运行输出截图:
核心报错原因
- 直接将读取到的RewriteRule原文作为sed匹配pattern传入,但RewriteRule本身包含
.、*、?、/、[]、()等大量正则特殊字符,sed默认会将这些字符解析为正则语法而非普通文本,直接传入会导致正则语法错误、匹配结果不符合预期 - 遍历文件的同时调用
sed -i直接修改正在读取的源文件,遍历过程中文件内容变更会导致行指针错位,容易出现漏删、错删 - 脚本开头全量读取三个文件内容赋值给变量的操作没有实际作用,大文件场景下会占用不必要的内存
- 变量引用未加双引号,路径或规则包含空格时会出现执行错误
推荐解决方案
优先使用awk实现整行精确匹配,无需手动转义特殊字符,仅需两次文件遍历即可完成全部匹配删除操作,性能远高于循环调用sed,也不会出现读写文件冲突问题。
操作前先备份测试文件:
cp "$file_path/test.txt" "$file_path/test.txt.bak"
替换原有逻辑为以下代码:
#!/bin/bash # 按需修改为你的文件存放路径 file_path="/path/to/your/htaccess/dir" # 执行删除:读取replace.txt中所有待删除的RewriteRule,整行匹配删除test.txt中对应行 awk ' NR==FNR { if ($0 ~ /RewriteRule/) { del_rules[$0] = 1 } next } { if (!del_rules[$0]) { print $0 } else { print "Found and deleted RewriteRule: " $0 > "/dev/stderr" } } ' "$file_path/replace.txt" "$file_path/test.txt" > "$file_path/test.txt.tmp" # 临时文件替换原文件 mv "$file_path/test.txt.tmp" "$file_path/test.txt"
如果需要保留原有循环+sed的写法,需要先对传入sed的匹配字符串做特殊字符转义,同时避免遍历过程中修改源文件,参考代码如下:
#!/bin/bash file_path="/path/to/your/htaccess/dir" cp "$file_path/test.txt" "$file_path/test.txt.bak" # 定义sed匹配串转义函数,转义所有正则特殊字符 sed_escape() { printf '%s\n' "$1" | sed 's/[][\/.$*?^()|+]/\\&/g' } # 先把所有待删除规则读入内存 declare -a to_del_rules=() while read -r replace_line; do if [[ $replace_line == *"RewriteRule"* ]]; then to_del_rules+=("$replace_line") fi done < "$file_path/replace.txt" # 遍历待删除规则执行sed删除 for rule in "${to_del_rules[@]}"; do echo "Found RewriteRule: $rule" escaped_rule=$(sed_escape "$rule") sed -i "/^${escaped_rule}$/d" "$file_path/test.txt" done
注意事项
- 匹配规则时建议加
^(行首锚定)和$(行尾锚定),避免误删部分内容重合的其他规则 - 所有变量引用统一加双引号,兼容包含空格、特殊字符的路径和规则内容
- 正式操作.htaccess前务必备份原文件,删除规则后验证站点重写逻辑正常
内容的提问来源于stack exchange,提问作者Dalton Sutton
相关产品推荐
相关产品推荐

