同一条命令在 bash 里正常,在 PowerShell 或 cmd 里失败
^ > < ! 和 % 这些几何字符同时也是 shell 元字符。PowerShell、cmd.exe 以及 .bat 文件里各自该怎么加引号。
你抄了一条在别人 Mac 上跑得好好的命令,在 Windows 上却什么都没发生、多出一个奇怪的文件、或者直接报错。
原因
ImageMagick 的几何后缀和 shell 语法撞车了:
| 字符 | ImageMagick 里的含义 | shell 里的含义 |
|---|---|---|
> | 仅当更大时缩小 | 把输出重定向到文件 |
< | 仅当更小时放大 | 从文件读取输入 |
^ | 填满目标框、允许溢出 | cmd.exe 的转义符 |
! | 强制精确尺寸 | 交互式 bash 的历史展开 |
% | 百分比,以及文件名计数器 | cmd / bat 里的变量展开 |
shell 会在 ImageMagick 看到它们之前就先处理掉。
实际会发生什么
magick in.jpg -resize 800x600> out.jpg
bash 把这条理解成「运行 magick in.jpg -resize 800x600,并把它的输出重定向到一个叫 out.jpg 的文件」。你会得到一张被无条件缩放的图,外加一个多余的空文件。不报错,但结果是错的 —— 这是最坑的一类失败。
各个 shell 的正确写法
# bash / zsh / sh
magick in.jpg -resize '800x600>' out.jpg
# PowerShell
magick in.jpg -resize '800x600>' out.jpg
# cmd.exe
magick in.jpg -resize "800x600>" out.jpg
bash 和 PowerShell 里单引号最保险,因为它会屏蔽所有解释。cmd.exe 根本不认识单引号,必须用双引号。
.bat 文件里的百分号问题
在 cmd.exe 里,加引号保护不了 %。写在 .bat 或 .cmd 文件里时,每个字面上的百分号都要写两遍:
REM 直接在命令行敲
magick in.jpg -crop 400x400 +repage tile_%02d.jpg
REM 写在 .bat 文件里
magick in.jpg -crop 400x400 +repage tile_%%02d.jpg
循环变量同理 —— 命令行里的 %f 在文件里要写成 %%f;identify -format 的格式字符串也一样。
括号
ImageMagick 的子表达式括号同样是 shell 敏感字符。老教程里为 bash 写的是转义:
magick in.jpg \( logo.png -resize 20% \) -composite out.jpg
这个反斜杠转义在 PowerShell 和 cmd 里都不管用。加引号则三种 shell 通用:
magick in.jpg '(' logo.png -resize 20% ')' -composite out.jpg
这就是本站的生成器采用这种写法的原因 —— 一种形式在三个 shell 里都正确。
PowerShell 的一些细节
- PowerShell 的参数拆分方式和 cmd 不同。如果一个参数里既有空格又有元字符,整个用单引号包起来。
- 双引号字符串里
$会被当成变量。任何含$的 ImageMagick 格式字符串请用单引号。 - PowerShell 7 处理原生命令参数比 5.1 更可预测。如果一条命令在 7 上能跑、5.1 上不能,通常就是引号的差别。
--%可以让 PowerShell 完全停止解析:magick --% in.jpg -resize 800x600^ out.jpg。比较粗暴,但整条粘贴长命令时很有用。
批量循环
# bash
mkdir -p out
for f in *.jpg; do magick "$f" -resize '1600>' "out/$f"; done
# PowerShell
New-Item -ItemType Directory -Force out | Out-Null
Get-ChildItem *.jpg | ForEach-Object { magick $_.FullName -resize '1600>' "out/$($_.Name)" }
# cmd.exe(命令行里这样写;.bat 文件里 % 要写两遍)
if not exist out mkdir out
for %f in (*.jpg) do magick "%f" -resize "1600>" "out\%~nxf"
本站页面顶部有 shell 选择器 —— 切换它,所有命令会自动按对应规则重新加引号。