batch_replace
递归批量替换当前目录及子目录下指定类型文件中的字符串,支持交互与传参模式。
一键脚本
bash <(curl -sL gitee.com/meimolihan/linux-command_sh/raw/master/batch_replace.sh) 'set -uo pipefail' 'set -euo pipefail' md
效果预览
脚本源码
不传参 = 进入交互模式
替换 sh + md 文件(默认类型)
./replace.sh "旧内容" "新内容"
只替换 txt 文件
./replace.sh "旧内容" "新内容" txt
同时替换 py、js、html 文件
./replace.sh "旧内容" "新内容" py js html
#!/bin/bash
set -uo pipefail
gl_hui='\033[38;5;59m'
gl_hong='\033[38;5;9m'
gl_lv='\033[38;5;10m'
gl_huang='\033[38;5;11m'
gl_lan='\033[38;5;32m'
gl_bai='\033[38;5;15m'
gl_zi='\033[38;5;13m'
gl_bufan='\033[38;5;14m'
log_info() { echo -e "${gl_lan}[信息]${gl_bai} $*"; }
log_ok() { echo -e "${gl_lv}[成功]${gl_bai} $*"; }
log_warn() { echo -e "${gl_huang}[警告]${gl_bai} $*"; }
log_error() { echo -e "${gl_hong}[错误]${gl_bai} $*" >&2; }
handle_invalid_input() {
echo -ne "\r${gl_huang}无效的输入,请重新输入! ${gl_zi} 1 ${gl_huang} 秒后返回"
sleep 1
echo -e "\r${gl_lv}无效的输入,请重新输入! ${gl_zi}0${gl_lv} 秒后返回"
sleep 0.5
return 2
}
handle_y_n() {
echo -e "${gl_hong}无效的选择,请输入 ${gl_bai}(${gl_lv}y${gl_bai}或${gl_hong}N${gl_bai})${gl_hong}。${gl_bai}"
sleep 1
echo -e "${gl_huang}无效的选择,请输入 ${gl_bai}(${gl_lv}y${gl_bai}或${gl_hong}N${gl_bai})${gl_huang}。${gl_bai}"
sleep 1
echo -e "${gl_lv}无效的选择,请输入 ${gl_bai}(${gl_lv}y${gl_bai}或${gl_hong}N${gl_bai})${gl_lv}。${gl_bai}"
sleep 0.5
return 2
}
break_end() {
echo -e "${gl_lv}操作完成${gl_bai}"
echo -e "${gl_bai}按任意键继续${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai} \c"
read -r -n 1 -s -r -p ""
echo ""
clear
}
exit_script() {
echo ""
echo -ne "${gl_hong}感谢使用,再见!${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
sleep_fractional 0.5
echo -ne "${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
sleep_fractional 0.6
clear
exit 0
}
exit_animation() {
echo -ne "${gl_lv}即将退出 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
sleep_fractional 0.5
echo -ne "${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
sleep_fractional 0.6
echo ""
clear
}
sleep_fractional() {
local seconds=$1
if sleep "$seconds" 2>/dev/null; then return 0; fi
if command -v perl >/dev/null 2>&1; then perl -e "select(undef, undef, undef, $seconds)"; return 0; fi
if command -v python3 >/dev/null 2>&1; then python3 -c "import time; time.sleep($seconds)"; return 0; fi
if command -v python >/dev/null 2>&1; then python -c "import time; time.sleep($seconds)"; return 0; fi
local int_seconds=$(echo "$seconds" | awk '{print int($1+0.999)}')
sleep "$int_seconds"
}
cancel_return() {
local menu_name="${1:-上一级选单}"
echo -e "${gl_lv}即将返回到 ${gl_huang}${menu_name}${gl_lv}${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai} \c"
sleep 0.6
echo ""
clear
}
show_help() {
clear
echo -e "${gl_zi}>>> 工具使用说明${gl_bai}"
echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
log_info "功能说明:递归替换当前目录下指定类型文件中的字符串"
echo -e ""
log_info "使用模式:"
log_info " 1. 交互模式:按提示输入 旧字符串、新字符串、文件类型"
log_info " 2. 传参模式:./脚本.sh 旧字符串 新字符串 文件类型"
echo -e ""
log_info "使用示例:"
log_info " 1. 替换 sh/md 文件:./replace.sh old new"
log_info " 2. 替换 txt/py 文件:./replace.sh old new txt py"
log_info " 3. 替换所有类型:./replace.sh old new sh md txt py js html"
echo -e ""
log_info "支持特性:"
log_info " - 兼容 Linux/macOS 系统"
log_info " - 支持任意特殊字符,无需转义"
log_info " - 自定义文件类型,空格分隔"
log_info " - 替换前二次确认,防止误操作"
echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
echo -e ""
}
batch_replace_str() {
clear
show_help
echo -e "${gl_zi}>>> 批量字符串替换工具${gl_bai}"
echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
local OLD_STR NEW_STR FILE_TYPES
if [ $# -ge 2 ]; then
OLD_STR="$1"
NEW_STR="$2"
FILE_TYPES="${3:-sh}"
log_info "已通过命令行传参获取配置"
else
read -r -e -p "$(echo -e "${gl_bai}请输入要替换的旧字符串(${gl_hong}0${gl_bai}退出): ")" OLD_STR
[ "$OLD_STR" = "0" ] && { exit_script; }
read -r -e -p "$(echo -e "${gl_bai}请输入替换后的新字符串(${gl_hong}0${gl_bai}退出): ")" NEW_STR
[ "$NEW_STR" = "0" ] && { exit_script; }
read -r -e -p "$(echo -e "${gl_bai}请输入要处理的文件后缀(${gl_huang}空格分隔,默认:sh${gl_bai})(${gl_hong}0${gl_bai}退出): ")" FILE_TYPES
[ "$FILE_TYPES" = "0" ] && { exit_script; }
FILE_TYPES=${FILE_TYPES:-sh md}
fi
if [[ -z "${OLD_STR}" || -z "${NEW_STR}" ]]; then
log_error "旧字符串和新字符串不能为空,请重新操作!"
sleep_fractional 1.5
batch_replace_str
return 1
fi
echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
log_info "替换配置预览"
log_info "旧内容:${gl_huang}${OLD_STR}${gl_bai}"
log_info "新内容:${gl_lv}${NEW_STR}${gl_bai}"
log_info "处理文件类型:${gl_lan}${FILE_TYPES}${gl_bai}"
log_warn "替换范围:当前目录及子目录下匹配的文件"
log_warn "替换模式:全局替换(所有匹配项都会被替换)"
read -r -e -p "$(echo -e "${gl_bai}是否确认执行替换?(${gl_lv}y${gl_bai}/${gl_hong}N${gl_bai}): ")" choice
case "${choice}" in
[Yy])
log_info "开始执行批量替换,请稍候${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
local sed_i=(-i)
if [[ "$(uname -s)" == "Darwin" ]]; then
sed_i=(-i "")
log_info "检测到 macOS 系统,自动适配 sed 语法"
fi
for ext in $FILE_TYPES; do
ext=${ext#.}
count=$(find . -type f -name "*.${ext}" | wc -l | tr -d ' ')
if [ "$count" -gt 0 ]; then
find . -type f -name "*.${ext}" -exec sed "${sed_i[@]}" "s#${OLD_STR}#${NEW_STR}#g" {} +
log_ok "已处理 ${count} 个 .${ext} 文件"
else
log_warn "未找到 .${ext} 文件,跳过该类型"
fi
done
log_ok "✅ 批量替换操作全部完成!"
;;
[Nn])
log_warn "已取消替换操作"
sleep_fractional 1
;;
*)
handle_y_n
batch_replace_str
return 1
;;
esac
echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
break_end
}
if [ $# -ge 2 ]; then
batch_replace_str "$1" "$2" "${*:3}"
else
batch_replace_str
fi
创建本地脚本
new_script="new_test.sh"
cat > "$new_script" << 'EOF'
#!/bin/bash
# 粘贴脚本源码
EOF
# 保留本地脚本,去掉 rm -f "$new_script"
chmod +x "$new_script" && ./"$new_script" && rm -f "$new_script"