批量修改文件安全的脚本

wen 实用脚本 2

本文目录导读:

批量修改文件安全的脚本

  1. 使用说明
  2. 安全特性
  3. 常见示例
#!/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:选择 renamecontentboth
  • RENAME_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

脚本会自动找到最近的备份并恢复。

安全特性

  1. 自动备份:修改前自动备份原文件
  2. 预览模式:先查看影响范围再执行
  3. 错误处理:重名冲突会跳过并警告
  4. 可恢复:支持一键恢复所有修改
  5. 无侵入:不会修改备份目录内的文件

常见示例

重命名示例

# 将 "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"

上一篇怎么用脚本转换时间单位

下一篇当前分类已是最新一篇

抱歉,评论功能暂时关闭!