本文目录导读:

#!/bin/bash
#
# 文件名: batch_file_modifier.sh
# 描述: 批量修改文件的安全脚本,支持预览、备份、正则匹配与恢复
# 使用方法:
# 1. 先预览: bash batch_file_modifier.sh preview
# 2. 执行修改: bash batch_file_modifier.sh apply
# 3. 恢复备份: bash batch_file_modifier.sh restore
#
set -euo pipefail
# ==================== 配置区域 (请根据需求修改) ====================
# 目标目录 (默认为当前目录,建议修改为具体路径)
TARGET_DIR="."
# 备份目录 (自动创建)
BACKUP_DIR="${TARGET_DIR}/.batch_file_backup_$(date +%Y%m%d_%H%M%S)"
# 要匹配的文件模式 (支持通配符)
# "*.txt", "*.md", "*.sh", "config.*"
FILE_PATTERN="*.txt"
# 要执行的操作类型: rename / content / both
# rename - 重命名文件
# content - 修改文件内容
# both - 同时执行两种操作
OPERATION_TYPE="rename"
# -------- 重命名规则 (当 OPERATION_TYPE=rename 或 both 时生效) --------
# 使用 sed 正则表达式替换文件名
# 格式: "s/查找模式/替换模式/g"
RENAME_PATTERN="s/old_prefix/new_prefix/g"
# -------- 内容修改规则 (当 OPERATION_TYPE=content 或 both 时生效) --------
# 使用 sed 正则表达式替换文件内容
# 格式: "s/查找模式/替换模式/g"
CONTENT_PATTERN="s/old_text/new_text/g"
# 是否递归处理子目录 (true/false)
RECURSIVE="true"
# 是否显示详细日志 (true/false)
VERBOSE="true"
# ==================== 函数定义 ====================
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# 日志函数
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_debug() { [[ "$VERBOSE" == "true" ]] && echo -e "${CYAN}[DEBUG]${NC} $1"; }
# 显示帮助信息
usage() {
echo "用法: $0 {preview|apply|restore|help}"
echo ""
echo "命令:"
echo " preview - 预览将要修改的文件 (不实际执行)"
echo " apply - 执行修改 (自动备份原文件)"
echo " restore - 从最近的备份恢复修改"
echo " help - 显示此帮助信息"
echo ""
echo "请先编辑脚本中的配置区域,然后运行 preview 确认无误后再执行 apply。"
exit 0
}
# 检查依赖
check_dependencies() {
local deps=("sed" "find" "cp" "mv" "mkdir" "date")
for cmd in "${deps[@]}"; do
if ! command -v "$cmd" &>/dev/null; then
log_error "缺少必需命令: $cmd"
exit 1
fi
done
}
# 生成备份文件路径
get_backup_path() {
local original_path="$1"
# 将绝对路径转换为相对备份路径 (去除开头的 / )
local relative_path="${original_path#/}"
echo "${BACKUP_DIR}/${relative_path}"
}
# 创建备份
create_backup() {
local file_path="$1"
local backup_path
backup_path="$(get_backup_path "$file_path")"
# 确保备份目录存在
mkdir -p "$(dirname "$backup_path")"
# 复制文件保留权限和时间戳
cp -p "$file_path" "$backup_path" 2>/dev/null || cp "$file_path" "$backup_path"
log_debug "已备份: $file_path -> $backup_path"
}
# 安全重命名文件
safe_rename() {
local old_path="$1"
local new_path="$2"
if [[ "$old_path" == "$new_path" ]]; then
return 0
fi
# 检查目标是否已存在
if [[ -e "$new_path" ]]; then
log_warn "目标文件已存在,跳过: $new_path"
return 1
fi
# 确保目标目录存在
mkdir -p "$(dirname "$new_path")" 2>/dev/null || true
mv "$old_path" "$new_path" 2>/dev/null
if [[ $? -eq 0 ]]; then
log_info "重命名: $old_path -> $new_path"
return 0
else
log_error "重命名失败: $old_path"
return 1
fi
}
# 获取符合条件的文件列表
get_file_list() {
local find_args=()
find_args+=("$TARGET_DIR")
find_args+=("-type" "f")
find_args+=("-name" "$FILE_PATTERN")
if [[ "$RECURSIVE" != "true" ]]; then
find_args+=("-maxdepth" "1")
fi
# 忽略备份目录
find_args+=("!" "-path" "${TARGET_DIR}/.batch_file_backup_*")
# 按文件名排序,保证可预测的顺序
find "${find_args[@]}" 2>/dev/null | sort
}
# 处理重命名操作
process_rename() {
local file="$1"
local dir
local base
local new_base
dir=$(dirname "$file")
base=$(basename "$file")
new_base=$(echo "$base" | sed "$RENAME_PATTERN")
if [[ "$base" != "$new_base" ]]; then
local new_path="${dir}/${new_base}"
echo "$file|$new_path"
fi
}
修改操作
process_content() {
local file="$1"
local temp_file
temp_file=$(mktemp)
# 检查是否包含匹配模式 (先预览)
if grep -q "$(echo "$CONTENT_PATTERN" | sed 's/^s\/\(.*\)\/.*\/.*$/\1/')" "$file" 2>/dev/null; then
echo "$file"
fi
rm -f "$temp_file"
}
# 预览模式
preview_mode() {
log_info "===== 预览模式 ====="
log_info "目标目录: $TARGET_DIR"
log_info "文件模式: $FILE_PATTERN"
log_info "操作类型: $OPERATION_TYPE"
echo ""
local files
files=$(get_file_list)
if [[ -z "$files" ]]; then
log_warn "没有找到匹配的文件。"
return
fi
local count=0
while IFS= read -r file; do
case "$OPERATION_TYPE" in
rename)
local result
result=$(process_rename "$file")
if [[ -n "$result" ]]; then
IFS='|' read -r old_path new_path <<< "$result"
log_info "将重命名: $old_path"
log_info " ->: $new_path"
((count++))
fi
;;
content)
local result
result=$(process_content "$file")
if [[ -n "$result" ]]; then
log_info "将修改内容: $file"
((count++))
fi
;;
both)
log_info "文件: $file"
local rename_result
rename_result=$(process_rename "$file")
if [[ -n "$rename_result" ]]; then
IFS='|' read -r old_path new_path <<< "$rename_result"
log_info " 重命名: $old_path -> $new_path"
fi
if process_content "$file" >/dev/null; then
log_info " 内容将被修改"
fi
((count++))
;;
esac
done <<< "$files"
echo ""
if [[ $count -eq 0 ]]; then
log_info "没有文件需要修改。"
else
log_info "共 $count 个文件将被修改。"
log_warn "运行 '$0 apply' 来执行修改,或 '$0 restore' 恢复备份。"
fi
}
# 执行模式
apply_mode() {
log_info "===== 执行模式 ====="
log_info "目标目录: $TARGET_DIR"
log_info "文件模式: $FILE_PATTERN"
log_info "操作类型: $OPERATION_TYPE"
# 创建备份目录
mkdir -p "$BACKUP_DIR"
log_info "备份目录: $BACKUP_DIR"
local files
files=$(get_file_list)
if [[ -z "$files" ]]; then
log_warn "没有找到匹配的文件。"
return
fi
local modified_count=0
local error_count=0
while IFS= read -r file; do
log_debug "处理文件: $file"
# 先备份
create_backup "$file"
case "$OPERATION_TYPE" in
rename)
local result
result=$(process_rename "$file")
if [[ -n "$result" ]]; then
IFS='|' read -r old_path new_path <<< "$result"
if safe_rename "$old_path" "$new_path"; then
((modified_count++))
else
((error_count++))
fi
fi
;;
content)
if process_content "$file" >/dev/null; then
if sed -i "$CONTENT_PATTERN" "$file" 2>/dev/null; then
log_info "修改内容: $file"
((modified_count++))
else
log_error "内容修改失败: $file"
((error_count++))
fi
fi
;;
both)
# 先修改内容
if process_content "$file" >/dev/null; then
sed -i "$CONTENT_PATTERN" "$file" 2>/dev/null && \
log_info "修改内容: $file" || \
log_error "内容修改失败: $file"
fi
# 再重命名
local result
result=$(process_rename "$file")
if [[ -n "$result" ]]; then
IFS='|' read -r old_path new_path <<< "$result"
if safe_rename "$old_path" "$new_path"; then
((modified_count++))
else
((error_count++))
fi
fi
;;
esac
done <<< "$files"
echo ""
log_info "修改完成: 成功 $modified_count, 失败 $error_count"
log_info "备份位置: $BACKUP_DIR"
log_warn "如需恢复,请运行 '$0 restore'"
}
# 恢复模式
restore_mode() {
log_info "===== 恢复模式 ====="
# 查找最近的备份目录
local backup_dirs
backup_dirs=$(find "$TARGET_DIR" -maxdepth 1 -type d -name ".batch_file_backup_*" | sort -r)
if [[ -z "$backup_dirs" ]]; then
log_error "没有找到备份目录。"
exit 1
fi
# 选择最近的备份
local latest_backup
latest_backup=$(echo "$backup_dirs" | head -1)
log_info "使用备份: $latest_backup"
if [[ ! -d "$latest_backup" ]]; then
log_error "备份目录不存在: $latest_backup"
exit 1
fi
local count=0
# 遍历备份文件并恢复
while IFS= read -r backup_file; do
# 计算原始路径
local relative_path="${backup_file#${latest_backup}/}"
local original_path="${TARGET_DIR}/${relative_path}"
# 确保目标目录存在
mkdir -p "$(dirname "$original_path")" 2>/dev/null || true
# 恢复文件
cp -p "$backup_file" "$original_path" 2>/dev/null || cp "$backup_file" "$original_path"
log_info "已恢复: $original_path"
((count++))
done < <(find "$latest_backup" -type f)
log_info "恢复完成,共恢复 $count 个文件。"
}
# ==================== 主程序 ====================
main() {
# 检查依赖
check_dependencies
# 处理命令行参数
case "${1:-help}" in
preview)
preview_mode
;;
apply)
if [[ ! -d "$TARGET_DIR" ]]; then
log_error "目标目录不存在: $TARGET_DIR"
exit 1
fi
apply_mode
;;
restore)
restore_mode
;;
help|--help|-h)
usage
;;
*)
log_error "未知命令: $1"
usage
;;
esac
}
main "$@"
使用说明
配置脚本
打开脚本,修改 配置区域 的变量:
TARGET_DIR:目标目录路径FILE_PATTERN:要处理的文件通配符OPERATION_TYPE:选择rename、content或bothRENAME_PATTERN/CONTENT_PATTERN:sed 正则替换规则
先预览(重要)
bash batch_file_modifier.sh preview
这将显示所有将要修改的文件,但不会实际执行。
执行修改
bash batch_file_modifier.sh apply
脚本会自动在目标目录下创建备份文件夹 .batch_file_backup_时间戳。
恢复备份
如果修改出错:
bash batch_file_modifier.sh restore
脚本会自动找到最近的备份并恢复。
安全特性
- 自动备份:修改前自动备份原文件
- 预览模式:先查看影响范围再执行
- 错误处理:重名冲突会跳过并警告
- 可恢复:支持一键恢复所有修改
- 无侵入:不会修改备份目录内的文件
常见示例
重命名示例
# 将 "report_2023.txt" 改为 "report_2024.txt" RENAME_PATTERN="s/2023/2024/g" FILE_PATTERN="*.txt"
替换示例
# 将文件中的 "http://" 替换为 "https://" CONTENT_PATTERN="s|http://|https://|g" OPERATION_TYPE="content" FILE_PATTERN="*.html"
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播