如何在PowerShell中修改路径中间节点?适配ImageMagick对比场景
用ImageMagick的SSIM算法对比图像的PowerShell脚本优化方案
目录结构
原始PNG图像与压缩后JPG图像的目录结构如下:
+ root | +- FooBar | | | +- 2023-10 | | \- *.png | \- 2023-11 | \- *.png \- FooBar.cmp | +- 2023-10 | \- *.jpg \- 2023-11 \- *.jpg
root\FooBar:存储原始PNG图像root\FooBar.cmp:存储压缩后的JPG图像- 文件名格式:
FooBar_%Y-%m-%d_%H-%M-%S.<millisecond>_<display_w>x<display_h>.<extension>,其中%开头字符对应C语言strftime格式化规则,<millisecond>为毫秒值,<display_w>/<display_h>为分辨率宽高,<extension>为png或jpg
现有尝试脚本
当前使用的PowerShell脚本如下:
Get-ChildItem .\root\FooBar -Recurse -Include "*.png" | % { $new = Join-Path -Path $_.Directory.Parent.Parent.FullName -ChildPath ($_.Directory.Parent.Name + ".cmp"); $new = Join-Path -Path $new -ChildPath $_.Directory.BaseName; $new = Join-Path -Path $new -ChildPath ($_.BaseName + ".jpg"); magick compare -metric SSIM $new $_ NUL 2>&1 | % ToString }
注:
2>&1 | % ToString用于规避隐式RemoteException,可忽略
需求与问题
现有脚本写法冗余,希望直接修改路径中间节点实现转换(例如将root\FooBar\2023-11\2023-11-01_02-03-04.567_890x1234.png转为root\FooBar.cmp\2023-11\2023-11-01_02-03-04.567_890x1234.jpg),需解决:
- PowerShell 5.1(版本信息如下)下的简洁实现方式
- 升级到最新PowerShell版本后的优化方案
PowerShell 5.1版本信息:
PS> $PSVersionTable Name Value ---- ----- PSVersion 5.1.19041.3570 PSEdition Desktop PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} BuildVersion 10.0.19041.3570 CLRVersion 4.0.30319.42000 WSManStackVersion 3.0 PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1
解决方案
PowerShell 5.1 实现方式
利用字符串替换直接修改路径节点和后缀,是最简洁的实现方式:
# 固定应用名称的写法 Get-ChildItem .\root\FooBar -Recurse -Filter *.png | ForEach-Object { # 替换路径中的FooBar节点为FooBar.cmp,同时替换后缀为jpg $cmpPath = $_.FullName -replace '\\FooBar\\', '\FooBar.cmp\' -replace '\.png$', '.jpg' # 执行SSIM对比 magick compare -metric SSIM $cmpPath $_.FullName NUL 2>&1 | ForEach-Object ToString }
如果FooBar是动态应用名称,可先定义变量再替换:
$appName = 'FooBar' # 替换为实际应用名称 Get-ChildItem ".\root\$appName" -Recurse -Filter *.png | ForEach-Object { $cmpPath = $_.FullName -replace "\\$appName\\", "\\$appName.cmp\\" -replace '\.png$', '.jpg' magick compare -metric SSIM $cmpPath $_.FullName NUL 2>&1 | ForEach-Object ToString }
这种写法避免了多层Join-Path嵌套,代码更直观易读。
最新PowerShell版本(7.x+)实现方式
PowerShell 7.x+对路径操作的支持更灵活,核心逻辑依然可以用字符串替换,写法与5.1一致且性能更优:
$appName = 'FooBar' Get-ChildItem ".\root\$appName" -Recurse -Filter *.png | ForEach-Object { $cmpPath = $_.FullName -replace "\\$appName\\", "\\$appName.cmp\\" -replace '\.png$', '.jpg' magick compare -metric SSIM $cmpPath $_.FullName NUL 2>&1 | ForEach-Object ToString }
如果偏好结构化路径拼接,也可以用更清晰的层级写法:
$appName = 'FooBar' Get-ChildItem ".\root\$appName" -Recurse -Filter *.png | ForEach-Object { $rootDir = $_.Directory.Parent.Parent.FullName $cmpRootDir = Join-Path $rootDir "$appName.cmp" $targetSubDir = Join-Path $cmpRootDir $_.Directory.BaseName $cmpPath = Join-Path $targetSubDir ($_.BaseName + '.jpg') magick compare -metric SSIM $cmpPath $_.FullName NUL 2>&1 | ForEach-Object ToString }
内容的提问来源于stack exchange,提问作者user-id-14900042
相关产品推荐
相关产品推荐

