筛选、备份及恢复本地电脑上的涉密CAD
- IT
- 6天前
- 30热度
- 0评论
行业特点决定了我们经常要处理涉密CAD,比如大范围的地形图。根据《保守国家秘密法》第二十九条、《计算机信息系统国际联网保密管理规定》第六条、《测绘成果管理条例》第四条等,存有保密文件的设备不得接入公共网络,同时还需定期接受相关部门的检查。
那么我们就需要经常自查,或至少在相关部门检查前开展自查。这项工作并不容易:因为可能涉密的CAD文件分散在整个硬盘内,人工检索费时费力。同时,如果将它们临时转移,每个文件又分属不同的目录,事后恢复非常困难。简单粗暴的方式是将整个工作文件夹均进行转移,但工作文件夹通常非常巨大,转移费时、费空间,同时还会影响正常业务的开展。
那么最好的办法是将“可能涉密的文件”筛选出来进行备份,同时保留其文件夹结构信息。一个powershell脚本可以做到这一点。
总体思路与关键逻辑
基本思路:
通过文件名匹配关键词和文件类型,找到对应的文件,并备份到指定文件夹内,同时保持目录树结构。例如,备份文件夹为E:\bak,要备份的文件是C:\dir\file.dwg,则备份后的文件应位于:
E:\bak\C\dir\file.dwg
基本设置:
1、用户应能指定文件名关键词,如“地形图”、“平面图”、“2000”等。
2、用户应能指定最小CAD文件大小,以命中所有大于某个体积的CAD文件。
3、用户应能指定备份文件夹。
4、用户的参数设定应友好,不应让用户去动脚本本身。
关键安全逻辑:
1、应校验备份文件夹是否存在,防止用户互相copy脚本直接执行,造成无法备份。
2、脚本应跳过备份文件夹本身,否则会陷入无限循环。
3、脚本应跳过用户映射的网络驱动器,只检索真正的本地分区。
4、需要考虑到潜在的文件权限问题,仅当备份成功时,才删除原始文件。
5、应输出完整的log日志文件。
操作友好性:
1、尽量避免用户进行命令行操作。
工具实现
设置文件:setting.txt
# 前5行是注释
# 3个参数设置如下示例:
# 备份路径="E:\My Backups" ← 如果路径带空格则加上英文双引号
# 关键词="地形", "dxt", "1万", "2000" ← 请用英文双引号、英文逗号分隔
# 最小文件大小(兆)=20
备份路径=
关键词="地形", "dxt", "1万", "2000", "平面", "总体", "1000", "用地", "缩图", "布置", "平纵"
最小文件大小(兆)=20
备份及删除:bak-del-dwg.ps1
function Get-Settings {
param(
[string]$File = (Join-Path $PSScriptRoot 'setting.txt')
)
if (-not (Test-Path $File)) {
throw "缺少配置文件:$File"
}
$cfg = @{}
# -Raw 能保留首行可能的 UTF-8 BOM,让后面一起 TrimStart
$lines = (Get-Content -Path $File -Raw).Split("`n")
foreach ($line in $lines) {
$L = $line.Trim() # 去首尾空白
$L = $L.TrimStart([char]0xFEFF) # 去掉 UTF-8/UTF-16 BOM
if ($L -eq '' -or $L -like '#*') { continue }
# 用全角=也能匹配
if ($L -match '^(.*?)\s*[==]\s*(.*)$') {
$key = $Matches[1].Trim()
$val = $Matches[2].Trim()
# 去首尾英文双引号
if ($val.StartsWith('"') -and $val.EndsWith('"')) {
$val = $val.Substring(1, $val.Length-2)
}
$val = $val.TrimEnd('\','/')
$cfg[$key] = $val
}
}
return $cfg
}
# ── 读取配置 ───────────────────────────────────────────
try {
$cfg = Get-Settings
$BackupRoot = $cfg['备份路径']
if (-not $BackupRoot) { throw "setting.txt 里 备份路径= 未设置" }
# 关键词行去掉引号再分割
$rawKW = $cfg['关键词']
if (-not $rawKW) { throw "setting.txt 里 关键词= 未设置" }
$Keywords = ($rawKW -replace '"','').Split(',') | ForEach-Object { $_.Trim() }
$MinSizeMB = [int]($cfg['最小文件大小(兆)'] -as [int])
$MinSizeBytes = $MinSizeMB * 1MB
} catch {
Write-Host $_.Exception.Message -ForegroundColor Red
exit 1
}
# ── 盘符与备份目录检查
try {
# 1) 备份盘符存在吗?
$driveRoot = ([IO.Path]::GetPathRoot($BackupRoot))
if (-not (Test-Path $driveRoot)) {
throw "备份盘符 $driveRoot 不存在,请检查setting.txt中的参数设置!"
}
# 2) 备份目录存在吗?不存在就尝试创建
if (-not (Test-Path $BackupRoot)) {
try {
New-Item -Path $BackupRoot -ItemType Directory -Force -ErrorAction Stop | Out-Null
} catch {
throw "无法创建备份目录:$BackupRoot"
}
}
}
catch {
Write-Host $_.Exception.Message -ForegroundColor Red
exit 1
}
# 新增日志文件
$LogFile = Join-Path $BackupRoot ("backup_{0:yyyyMMdd_HHmmss}.log" -f (Get-Date))
function Write-Log {
param(
[string]$Message,
[string]$Level = "INFO"
)
$timestamp = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
$line = "[{0}] [{1}] {2}" -f $timestamp, $Level, $Message
# 同步输出到控制台和日志文件
Write-Host $line
Add-Content -Path $LogFile -Value $line -ErrorAction SilentlyContinue
}
Write-Log "=== 开始备份 (MinSizeMB=$MinSizeMB) ==="
# 获取所有本地分区
$drives = Get-PSDrive -PSProvider FileSystem | Where-Object {
$_.DisplayRoot -notlike '\\*' # 过滤掉映射网络盘
}
foreach ($drive in $drives) {
Write-Host "正在扫描 $($drive.Root)..."
Get-ChildItem -Path $drive.Root -Recurse -Filter *.dwg -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -notlike "$BackupRoot*" } |
ForEach-Object {
$file = $_
$filename = $file.Name
if (
($keywords | Where-Object { $filename -like "*$_*" }) -or
($file.Length -gt $minSizeBytes)
) {
# 构建路径,保留原始分区字母作为第一层
$relativePath = $file.FullName -replace "^([A-Z]):", '$1'
$destPath = Join-Path $BackupRoot $relativePath
# 确保目录存在
$destDir = Split-Path $destPath
if (-not (Test-Path $destDir)) {
New-Item -Path $destDir -ItemType Directory -Force | Out-Null
}
try {
Copy-Item -Path $file.FullName -Destination $destPath -Force -ErrorAction Stop
Write-Log "已备份: $($file.FullName) "
} catch {
Write-Log "备份失败: $($file.FullName) —— $_" "ERROR"
continue # 跳过当前文件
}
# 删除源文件
try {
Remove-Item -Path $file.FullName -Force -ErrorAction Stop
Write-Log "已删除源文件: $($file.FullName)"
} catch {
Write-Log "删除失败: $($file.FullName) —— $_" "ERROR"
}
Write-Host "已备份并删除: $($file.FullName)"
}
}
}
Write-Log "=== 备份完成 ==="
恢复:restore-dwg.ps1
function Get-Settings {
param(
[string]$File = (Join-Path $PSScriptRoot 'setting.txt')
)
if (-not (Test-Path $File)) {
throw "缺少配置文件:$File"
}
$cfg = @{}
# -Raw 能保留首行可能的 UTF-8 BOM,让后面一起 TrimStart
$lines = (Get-Content -Path $File -Raw).Split("`n")
foreach ($line in $lines) {
$L = $line.Trim() # 去首尾空白
$L = $L.TrimStart([char]0xFEFF) # 去掉 UTF-8/UTF-16 BOM
if ($L -eq '' -or $L -like '#*') { continue }
# 用全角=也能匹配
if ($L -match '^(.*?)\s*[==]\s*(.*)$') {
$key = $Matches[1].Trim()
$val = $Matches[2].Trim()
# 去首尾英文双引号
if ($val.StartsWith('"') -and $val.EndsWith('"')) {
$val = $val.Substring(1, $val.Length-2)
}
$val = $val.TrimEnd('\','/')
$cfg[$key] = $val
}
}
return $cfg
}
# ── 读取配置 ───────────────────────────────────────────
try {
$cfg = Get-Settings
$BackupRoot = $cfg['备份路径']
if (-not $BackupRoot) {
throw 'setting.txt 里 备份路径= 未设置'
}
# 仅用于“还原”脚本:备份目录必须真实存在
if (-not (Test-Path $BackupRoot)) {
throw "备份路径 $BackupRoot 不存在,无法还原,请检查setting.txt中的参数设置!"
}
}
catch {
Write-Host $_.Exception.Message -ForegroundColor Red
exit 1
}
# ── 日志文件 ───────────────────────────────────────────
$LogFile = Join-Path $BackupRoot ("restore_{0:yyyyMMdd_HHmmss}.log" -f (Get-Date))
function Write-Log {
param(
[string]$Message,
[string]$Level = "INFO"
)
$ts = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$line = "[${ts}] [$Level] $Message"
Write-Host $line
Add-Content -Path $LogFile -Value $line -ErrorAction SilentlyContinue
}
Write-Log "=== 开始还原 ==="
# 获取所有 .dwg 文件
Get-ChildItem -Path $BackupRoot -Recurse -Filter *.dwg | ForEach-Object {
$backupFile = $_
$relativePath = $backupFile.FullName.Substring($BackupRoot.Length).TrimStart('\') # D\xxx\xxx\file.dwg
# 防御性判断:确保第一部分是 A~Z 盘符字母
if ($relativePath -match '^[A-Z]\\') {
# 将路径还原成原始路径 D:\xxx\xxx\file.dwg
$driveLetter = $relativePath.Substring(0,1)
$restOfPath = $relativePath.Substring(1) # 去掉 D
$originalPath = "${driveLetter}:$restOfPath"
# 再保险一点:避免还原到 备份目录 自己
if ($originalPath -like "$BackupRoot*") {
Write-Log "跳过自身文件防止死循环: $originalPath"
continue
}
try {
# 确保目录存在
$originalDir = Split-Path $originalPath
if (-not (Test-Path $originalDir)) {
New-Item -Path $originalDir -ItemType Directory -Force | Out-Null
}
# 复制(或移动)文件回原处
Copy-Item -Path $backupFile.FullName -Destination $originalPath -Force -ErrorAction Stop
Write-Log "已还原: $originalPath"
} catch {
Write-Log "还原失败: $originalPath —— $_" "ERROR"
continue # 只跳过当前文件
}
}
else {
Write-Log "非法备份路径,跳过: $relativePath" "WARN"
}
}
Write-Log "=== 全部还原完成 ==="
bat启动备份脚本:1-带路径备份并删除原文件.bat
@echo off
setlocal enabledelayedexpansion
echo.
echo 即将开始备份,请确认已正确配置 setting.txt
pause
rem 当前目录下的 ps1
set "script_dir=%~dp0"
set "ps1_path=%script_dir%bak-del-dwg.ps1"
rem 以管理员启动 PowerShell ;-NoExit 保持窗口, -Wait 让 bat 等待
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
"Start-Process powershell -Verb RunAs -Wait -ArgumentList '-NoProfile','-ExecutionPolicy','Bypass','-NoExit','-File','\"%ps1_path%\"'"
pause
endlocal
bat启动恢复脚本:2-恢复备份文件至原路径.bat
@echo off
setlocal enabledelayedexpansion
echo.
echo 即将开始恢复,请确认已正确配置 setting.txt
pause
rem 当前目录下的 ps1
set "script_dir=%~dp0"
set "ps1_path=%script_dir%restore-dwg.ps1"
rem 以管理员启动 PowerShell ;-NoExit 保持窗口, -Wait 让 bat 等待
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
"Start-Process powershell -Verb RunAs -Wait -ArgumentList '-NoProfile','-ExecutionPolicy','Bypass','-NoExit','-File','\"%ps1_path%\"'"
pause
endlocal
使用方法
文件准备
将上述5个文件放在同一个文件夹内。
1-带路径备份并删除原文件.bat
2-恢复备份文件至原路径.bat
bak-del-dwg.ps1
restore-dwg.ps1
setting.txt
环境设置
首先,如果用户没有用powershell执行过任何脚本(很可能),那么需要先做一个环境设定。
以管理员身份打开powershell,并执行:
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
当弹出yes or no时,输入y,回车。这将允许powershell执行本地脚本。这个操作只需要执行1次。
参数设置
妥善编辑setting.txt。
工具使用
接下来,双击“1-带路径备份并删除原文件.bat”,即可以管理员身份执行备份脚本;双击“2-恢复备份文件至原路径.bat”,即可以管理员身份执行恢复脚本。
恢复脚本不会删除备份文件夹中的文件。过程中生成的log文件也位于备份文件夹内。
相关工具下载:点击下载