或是偷懒,或是相机直出的JPG已足够支持分享社交媒体的后期,旅行时往往直接用jpg修图,回家存入NAS的时候又舍不得RAW文件,30FPS下RAW文件可以说时拍了一堆,于是求助AI做了个简单的powershell命令行工具,以期从海量的RAW文件中提取那些对应已经精选过的jpg的原图。

  • 需要exiftool这个牛逼工具的支持

  • 受制于读卡速度,第3步比较慢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# ==============================================================================
# 脚本名称:基于 EXIF 拍摄时间戳的 RAW 文件自动找回工具
# 适用场景:JPG 已重命名或从 LR/剪映导出,需要根据原始拍摄时间找回对应的 RAW (CR3/ARW/NEF)
# ==============================================================================

# ----------------- 【1. 用户配置区 - 请根据需求修改】 -----------------

# [必须修改] 已导出的 JPG 文件夹路径
$srcJpgDir = "Z:\XXXX\XXXX\XX"

# [必须修改] 存储卡或原始 RAW 备份文件夹路径
$rawStorageDir = "F:\XXXX\XXXX"

# [必须修改] 筛选出来的 RAW 文件存放的目标文件夹
$destDir = "D:\XXXX"

# [可选修改] 原始 RAW 文件的后缀名(例如 .cr3, .arw, .nef, .dng)
$rawExtension = "*.cr3"

# ----------------- 【2. 初始化检查】 -----------------

# 检查 ExifTool 是否安装
if (!(Get-Command exiftool -ErrorAction SilentlyContinue)) {
Write-Host "错误:未发现 exiftool,请将其加入环境变量或放在脚本同目录。" -ForegroundColor Red
pause
exit
}

# 创建目标文件夹
if (!(Test-Path $destDir)) {
New-Item -Path $destDir -ItemType Directory | Out-Null
Write-Host "已创建目标文件夹: $destDir" -ForegroundColor Gray
}

# ----------------- 【3. 建立 RAW 索引 (第一阶段)】 -----------------

Write-Host "`n[1/2] 正在扫描原始 RAW 目录并建立时间戳索引..." -ForegroundColor Cyan
$rawIndex = @{}
$rawFiles = Get-ChildItem -Path $rawStorageDir -Filter $rawExtension -Recurse

$totalRaw = $rawFiles.Count
$count = 0

foreach ($file in $rawFiles) {
$count++
# 更新蓝色进度条
$percent = [math]::Round(($count / $totalRaw) * 100)
Write-Progress -Activity "建立 RAW 索引中" -Status "正在读取: $($file.Name)" -PercentComplete $percent

# 提取 RAW 的拍摄时间戳
$dt = & exiftool -s3 -DateTimeOriginal "$($file.FullName)"

if ($dt) {
# 考虑到连拍,一个时间戳可能对应多个文件,使用数组存储
if ($rawIndex.ContainsKey($dt)) {
$rawIndex[$dt] += ,$file.FullName
} else {
$rawIndex[$dt] = @($file.FullName)
}
}
}

# ----------------- 【4. 匹配并执行复制 (第二阶段)】 -----------------

Write-Host "[2/2] 正在比对 JPG 并在索引中查找匹配项..." -ForegroundColor Cyan
$jpgFiles = Get-ChildItem -Path $srcJpgDir -Filter *.jpg

$totalJpg = $jpgFiles.Count
$jCount = 0
$matchSuccess = 0

foreach ($jpg in $jpgFiles) {
$jCount++
$jPercent = [math]::Round(($jCount / $totalJpg) * 100)
Write-Progress -Activity "正在比对并复制文件" -Status "当前处理: $($jpg.Name)" -PercentComplete $jPercent

# 提取 JPG 的拍摄时间
$dtJpg = & exiftool -s3 -DateTimeOriginal "$($jpg.FullName)"

if ($dtJpg -and $rawIndex.ContainsKey($dtJpg)) {
foreach ($rawPath in $rawIndex[$dtJpg]) {
$rawName = Split-Path $rawPath -Leaf
Write-Host "[$jCount/$totalJpg] 命中! -> $rawName" -ForegroundColor Green
Copy-Item -Path $rawPath -Destination $destDir -Force
$matchSuccess++
}
} else {
Write-Host "[$jCount/$totalJpg] 未匹配: $($jpg.Name)" -ForegroundColor Gray
}
}

# ----------------- 【5. 结束报告】 -----------------

Write-Progress -Activity "正在比对并复制文件" -Completed
Write-Host "`n===============================================" -ForegroundColor White
Write-Host "任务完成!" -ForegroundColor Cyan
Write-Host "共处理 JPG: $totalJpg 张"
Write-Host "成功找回 RAW: $matchSuccess 份"
Write-Host "输出目录: $destDir"
Write-Host "===============================================" -ForegroundColor White
Write-Host "按任意键退出..."
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

另外AI还提供了个极速版,思路是批量读取 (Batch Processing),不再逐个文件调用 exiftool,而是改为 JPG 扫一次,RAW 扫一次,避免进程反复启动: 减少上千次 exiftool.exe 启动的系统延迟。下次有机会再试下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# ----------------- 【1. 用户配置区】 -----------------
$srcJpgDir = "Z:\XXXX\XXXX\XXXX"
$rawStorageDir = "F:\XXXX\XXXX"
$destDir = "D:\XXXX"
$rawExt = "cr3" # 只需写后缀名

# ----------------- 【2. 极速索引逻辑】 -----------------

# 第一步:批量扫描 RAW / Step 1: Batch Scan RAWs
Write-Host "正在极速索引存储卡 (F:\)..." -ForegroundColor Cyan
$rawIndex = @{}
# 关键:通过 exiftool 一次性导出所有 RAW 的路径和时间戳
$rawResults = & exiftool -r -ext $rawExt -s3 -p "`$Directory/`$FileName|`$DateTimeOriginal" "$rawStorageDir"

foreach ($line in $rawResults) {
$parts = $line.Split('|')
if ($parts.Count -eq 2) {
$path = $parts[0]; $time = $parts[1]
if ($rawIndex.ContainsKey($time)) { $rawIndex[$time] += ,$path } else { $rawIndex[$time] = @($path) }
}
}

# 第二步:批量扫描 JPG / Step 2: Batch Scan JPGs
Write-Host "正在极速索引导出目录 (Z:\)..." -ForegroundColor Cyan
$jpgIndex = @{}
# 关键:通过 exiftool 一次性获取所有 JPG 的时间戳
$jpgResults = & exiftool -ext jpg -s3 -p "`$Directory/`$FileName|`$DateTimeOriginal" "$srcJpgDir"

# ----------------- 【3. 内存比对与复制】 -----------------

$matchCount = 0
foreach ($line in $jpgResults) {
$parts = $line.Split('|')
if ($parts.Count -eq 2) {
$jpgPath = $parts[0]; $time = $parts[1]

if ($rawIndex.ContainsKey($time)) {
foreach ($rPath in $rawIndex[$time]) {
$name = Split-Path $rPath -Leaf
Write-Host "[命中] $time -> $name" -ForegroundColor Green
Copy-Item -Path $rPath -Destination $destDir -Force -ErrorAction SilentlyContinue
$matchCount++
}
}
}
}

Write-Host "`n任务完成!成功找回 $matchCount 个文件。" -ForegroundColor Cyan