如何用脚本实现文件快照

wen 实用脚本 2

本文目录导读:

如何用脚本实现文件快照

  1. Python 实现完整文件快照
  2. Bash Shell 脚本版本(简单快速)
  3. PowerShell 脚本(Windows)
  4. 使用说明

Python 实现完整文件快照

#!/usr/bin/env python3
# file_snapshot.py - 文件快照工具
import os
import hashlib
import json
import time
import argparse
from datetime import datetime
from pathlib import Path
class FileSnapshot:
    def __init__(self, base_path, exclude_patterns=None):
        self.base_path = Path(base_path)
        self.exclude_patterns = exclude_patterns or ['.git', 'node_modules', '__pycache__', '.DS_Store']
        self.snapshot_data = {}
    def compute_hash(self, file_path, block_size=65536):
        """计算文件哈希值"""
        sha256 = hashlib.sha256()
        try:
            with open(file_path, 'rb') as f:
                while chunk := f.read(block_size):
                    sha256.update(chunk)
            return sha256.hexdigest()
        except (IOError, PermissionError) as e:
            return f"ERROR: {str(e)}"
    def get_file_info(self, file_path):
        """获取文件详细信息"""
        stat = file_path.stat()
        return {
            'path': str(file_path.relative_to(self.base_path)),
            'size': stat.st_size,
            'mtime': stat.st_mtime,
            'hash': self.compute_hash(file_path),
            'permissions': oct(stat.st_mode & 0o777)
        }
    def create_snapshot(self):
        """创建文件快照"""
        for root, dirs, files in os.walk(self.base_path):
            # 排除特定目录
            dirs[:] = [d for d in dirs if d not in self.exclude_patterns]
            root_path = Path(root)
            for file in files:
                file_path = root_path / file
                if file not in self.exclude_patterns:
                    try:
                        file_info = self.get_file_info(file_path)
                        self.snapshot_data[file_info['path']] = file_info
                    except Exception as e:
                        print(f"Error processing {file_path}: {e}")
        return {
            'name': 'file_snapshot',
            'created_at': datetime.now().isoformat(),
            'base_path': str(self.base_path),
            'file_count': len(self.snapshot_data),
            'files': self.snapshot_data
        }
    def save_snapshot(self, output_file):
        """保存快照到文件"""
        snapshot = self.create_snapshot()
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(snapshot, f, indent=2, ensure_ascii=False)
        print(f"Snapshot saved to {output_file}")
        return snapshot
    def compare_snapshots(self, old_snapshot_file, new_snapshot_file=None):
        """比较两个快照"""
        with open(old_snapshot_file, 'r', encoding='utf-8') as f:
            old_snapshot = json.load(f)
        if new_snapshot_file:
            with open(new_snapshot_file, 'r', encoding='utf-8') as f:
                new_snapshot = json.load(f)
        else:
            new_snapshot = self.create_snapshot()
        changes = {
            'added': [],
            'modified': [],
            'removed': []
        }
        old_files = old_snapshot['files']
        new_files = new_snapshot['files']
        # 检查新增和修改的文件
        for file_path, file_info in new_files.items():
            if file_path not in old_files:
                changes['added'].append(file_path)
            elif file_info['hash'] != old_files[file_path]['hash']:
                changes['modified'].append({
                    'file': file_path,
                    'old_size': old_files[file_path]['size'],
                    'new_size': file_info['size'],
                    'old_mtime': old_files[file_path]['mtime'],
                    'new_mtime': file_info['mtime']
                })
        # 检查删除的文件
        for file_path in old_files:
            if file_path not in new_files:
                changes['removed'].append(file_path)
        return changes
    def restore_from_snapshot(self, snapshot_file, target_dir):
        """从快照恢复到目标目录"""
        with open(snapshot_file, 'r', encoding='utf-8') as f:
            snapshot = json.load(f)
        target_path = Path(target_dir)
        target_path.mkdir(parents=True, exist_ok=True)
        for file_path, file_info in snapshot['files'].items():
            destino = target_path / file_path
            source = self.base_path / file_path
            if source.exists():
                destino.parent.mkdir(parents=True, exist_ok=True)
                import shutil
                shutil.copy2(source, destino)
                print(f"Restored: {file_path}")
# 命令行接口
def main():
    parser = argparse.ArgumentParser(description='文件快照工具')
    parser.add_argument('action', choices=['create', 'compare', 'restore', 'monitor'],
                       help='操作类型')
    parser.add_argument('--path', required=True, help='要快照的目录路径')
    parser.add_argument('--output', help='快照文件路径')
    parser.add_argument('--old-snapshot', help='旧快照文件(用于比较)')
    parser.add_argument('--target', help='恢复目标目录')
    args = parser.parse_args()
    snapshotter = FileSnapshot(args.path)
    if args.action == 'create':
        output = args.output or f'snapshot_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json'
        snapshotter.save_snapshot(output)
    elif args.action == 'compare':
        if not args.old_snapshot:
            print("需要指定 --old-snapshot 参数")
            return
        changes = snapshotter.compare_snapshots(args.old_snapshot)
        print(f"新增文件: {len(changes['added'])}")
        print(f"修改文件: {len(changes['modified'])}")
        print(f"删除文件: {len(changes['removed'])}")
    elif args.action == 'restore':
        if not args.old_snapshot or not args.target:
            print("需要指定 --old-snapshot 和 --target 参数")
            return
        snapshotter.restore_from_snapshot(args.old_snapshot, args.target)
    elif args.action == 'monitor':
        # 监控模式
        import time
        print(f"监控 {args.path} ... (Ctrl+C 停止)")
        snap1 = snapshotter.create_snapshot()
        try:
            while True:
                time.sleep(5)
                snap2 = snapshotter.create_snapshot()
                if snap1['files'] != snap2['files']:
                    print(f"[{datetime.now().strftime('%H:%M:%S')}] 文件发生变化")
                snap1 = snap2
        except KeyboardInterrupt:
            print("\n监控停止")
if __name__ == '__main__':
    main()

Bash Shell 脚本版本(简单快速)

#!/bin/bash
# file_snapshot.sh - 简单文件快照
# 配置文件
SNAPSHOT_DIR="./snapshots"
LOG_FILE="./snapshot.log"
# 初始化
mkdir -p "$SNAPSHOT_DIR"
timestamp=$(date +"%Y%m%d_%H%M%S")
snapshot_file="$SNAPSHOT_DIR/snapshot_$timestamp.txt"
# 生成 MD5 校验清单
echo "生成文件快照: $snapshot_file"
echo "[$timestamp] 开始生成快照" >> "$LOG_FILE"
# 遍历文件并计算哈希
find . -type f ! -path "*/node_modules/*" ! -path "*/.git/*" | while read -r file; do
    md5=$(md5sum "$file" | cut -d' ' -f1)
    size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file")
    echo "$md5|$size|$file" >> "$snapshot_file"
done
echo "快照完成"
echo "[$timestamp] 快照完成: $snapshot_file" >> "$LOG_FILE"
# 比较函数
compare_snapshots() {
    old_snapshot=$1
    new_snapshot=$2
    if [ ! -f "$old_snapshot" ] || [ ! -f "$new_snapshot" ]; then
        echo "错误:快照文件不存在"
        exit 1
    fi
    echo "新增文件:"
    comm -13 <(cut -d'|' -f3 "$old_snapshot" | sort) <(cut -d'|' -f3 "$new_snapshot" | sort)
    echo "删除文件:"
    comm -23 <(cut -d'|' -f3 "$old_snapshot" | sort) <(cut -d'|' -f3 "$new_snapshot" | sort)
    echo "修改文件:"
    # 类似文件但MD5不同
    grep -f <(cut -d'|' -f3 "$old_snapshot") "$new_snapshot" | sort > /tmp/new_sorted.txt
    grep -f <(cut -d'|' -f3 "$new_snapshot") "$old_snapshot" | sort > /tmp/old_sorted.txt
    diff /tmp/old_sorted.txt /tmp/new_sorted.txt | grep "^[<>]" | grep -v "^\*\*\*"
}
# 主菜单
case "$1" in
    create)
        ;;
    compare)
        if [ $# -eq 3 ]; then
            compare_snapshots "$2" "$3"
        else
            echo "用法: $0 compare <旧快照> <新快照>"
        fi
        ;;
    *)
        echo "用法: $0 {create|compare <old> <new>}"
        ;;
esac

PowerShell 脚本(Windows)

# file_snapshot.ps1 - Windows 文件快照
param(
    [string]$Path = ".",
    [string]$Action = "create",
    [string]$OutputFile = "",
    [string]$ComparisonFile = ""
)
class FileSnapshot {
    [string]$Path
    [string]$Timestamp
    [int]$FileCount = 0
    [hashtable]$Files = @{}
    FileSnapshot([string]$path) {
        $this.Path = $path
        $this.Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    }
    [void]Create() {
        Get-ChildItem $this.Path -Recurse -File -Exclude *.dll,*.exe | ForEach-Object {
            try {
                $hash = Get-FileHash $_.FullName -Algorithm MD5
                $relativePath = $_.FullName.Replace((Resolve-Path $this.Path).Path + "\", "")
                $this.Files[$relativePath] = @{
                    Hash = $hash.Hash
                    Size = $_.Length
                    Modified = $_.LastWriteTime.Ticks
                    FullPath = $_.FullName
                }
                $this.FileCount++
            } catch {}
        }
    }
    [string]ToJson() {
        $this | ConvertTo-Json -Depth 3
    }
    [hashtable]Compare([FileSnapshot]$other) {
        $result = @{
            Added = @()
            Modified = @()
            Removed = @()
        }
        # 检查新增和修改
        foreach ($file in $other.Files.Keys) {
            if (-not $this.Files.ContainsKey($file)) {
                $result.Added += $file
            } elseif ($this.Files[$file].Hash -ne $other.Files[$file].Hash) {
                $result.Modified += $file
            }
        }
        # 检查删除
        foreach ($file in $this.Files.Keys) {
            if (-not $other.Files.ContainsKey($file)) {
                $result.Removed += $file
            }
        }
        return $result
    }
    [void]Save([string]$filename) {
        $json = $this.ToJson()
        $filename = $filename -replace '[^a-zA-Z0-9\-_\.]', '_'
        $filename = "snapshot_$(Get-Date -Format 'yyyyMMdd_HHmmss').json"
        $json | Out-File $filename -Encoding utf8
        Write-Host "快照已保存: $filename"
    }
    static [FileSnapshot]Load([string]$filename) {
        $json = Get-Content $filename -Raw
        return $json | ConvertFrom-Json
    }
}
# 主程序
switch ($Action) {
    "create" {
        $snapshot = [FileSnapshot]::new((Resolve-Path $Path).Path)
        $snapshot.Create()
        $snapshot.Save($OutputFile)
        Write-Host "创建快照完成: $((Resolve-Path $Path).Path) - $($snapshot.FileCount) 个文件"
    }
    "compare" {
        if ($ComparisonFile) {
            $oldSnapshot = [FileSnapshot]::Load($ComparisonFile)
            $newSnapshot = [FileSnapshot]::new((Resolve-Path $Path).Path)
            $newSnapshot.Create()
            $changes = $oldSnapshot.Compare($newSnapshot)
            Write-Host "=== 文件变化分析 ==="
            Write-Host "新增文件: $($changes.Added.Count)"
            $changes.Added | ForEach-Object { Write-Host "  + $_" }
            Write-Host "修改文件: $($changes.Modified.Count)"
            $changes.Modified | ForEach-Object { Write-Host "  ~ $_" }
            Write-Host "删除文件: $($changes.Removed.Count)"
            $changes.Removed | ForEach-Object { Write-Host "  - $_" }
        } else {
            Write-Error "比较模式需要指定 ComparisonFile 参数"
        }
    }
    "monitor" {
        Write-Host "开始监控 $Path ... (Ctrl+C 停止)"
        $firstRun = $true
        $oldSnapshot = $null
        while ($true) {
            $newSnapshot = [FileSnapshot]::new((Resolve-Path $Path).Path)
            $newSnapshot.Create()
            if ($firstRun) {
                $oldSnapshot = $newSnapshot
                $firstRun = $false
            } else {
                $changes = $oldSnapshot.Compare($newSnapshot)
                if (($changes.Added.Count + $changes.Modified.Count + $changes.Removed.Count) -gt 0) {
                    Write-Host "[$(Get-Date -Format 'HH:mm:ss')] 检测到文件变化:"
                    $changes.Added | ForEach-Object { Write-Host "  + 新增: $_" }
                    $changes.Modified | ForEach-Object { Write-Host "  ~ 修改: $_" }
                    $changes.Removed | ForEach-Object { Write-Host "  - 删除: $_" }
                }
                $oldSnapshot = $newSnapshot
            }
            Start-Sleep -Seconds 5
        }
    }
}

使用说明

Python 版本(推荐)

# 创建快照
python file_snapshot.py create --path /your/directory --output snapshot.json
# 比较快照
python file_snapshot.py compare --path /your/directory --old-snapshot old.json
# 恢复快照
python file_snapshot.py restore --path /source/directory --old-snapshot snapshot.json --target /restore/directory
# 实时监控
python file_snapshot.py monitor --path /your/directory

Bash 版本

# 创建快照
./file_snapshot.sh create
# 比较两个快照
./file_snapshot.sh compare old_snapshot.txt new_snapshot.txt

PowerShell 版本

# 创建快照
.\file_snapshot.ps1 -Path "C:\your\directory" -Action create
# 比较快照
.\file_snapshot.ps1 -Path "C:\your\directory" -Action compare -ComparisonFile "snapshot_20240101_120000.json"
# 实时监控
.\file_snapshot.ps1 -Path "C:\your\directory" -Action monitor

这些脚本都能实现基本的文件快照功能,Python版本功能最完整,支持安全、修改、恢复等功能;Bash版本适合Linux/Mac系统快速使用;PowerShell适合Windows环境,选择哪个取决于你的具体需求和使用环境。

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