Windows 命令行 ImageMagick 百分比转义

Windows 命令行 ImageMagick 百分比转义

我正在尝试在 Windows 7 的命令行中使用 ImageMagick 一次转换多张图片。我想使用 -set 选项界面,但得到了意想不到的结果。以下是我看到的一些奇怪现象的简短示例:

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world "%[filename:foo]_%[filename:bar].png"
example.jpg=>helloworld.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.125u 0:00.135

请注意,下划线字符从输出文件名中被神秘地删除了。我可以使用插入符号 (^) 来转义第二个百分号,以保留下划线:

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world "%[filename:foo]_^%[filename:bar].png"
example.jpg=>hello_world.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.125u 0:00.126

这是怎么回事?为什么插入插入符号会导致下划线被保留?插入符号 (^) 是否是尝试的正确转义符?此行为是由于命令 shell 环境变量扩展引起的,还是由于 ImageMagick 处理百分比转义的方式导致的怪异?无论哪种方式,在命令行参数中使用百分比符号是否存在可以遵循的一般原则,以导致可预测的行为?

我实际上想要做的是上述命令的一个版本,其中设置了更多属性,可以处理带有特殊字符(如空格和百分号)的文件名,但如果我无法理解这个简单得多的示例,那么尝试这样做似乎很愚蠢。最终,我需要从 .NET 程序执行该命令。

我看过http://www.robvanderwoude.com/escapechars.php在命令行中转义文件/文件夹名称中的 %但不知道如何将该建议应用到我的问题上。

更新

添加更多示例来回答@dbenham 和@Synetech 评论中的问题。

将下划线从第二个百分号之前(如上例所示)移动到下划线之前,会生成一个名为的文件hello^world.png

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world "%[filename:foo]^_%[filename:bar].png"
example.jpg=>hello^world.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.141u 0:00.132

使用脱字符号转义周围的引号始终会导致helloworld.png,无论脱字符号放在何处:

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world ^"^%[filename:foo]_^%[filename:bar].png^"
example.jpg=>helloworld.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.109u 0:00.115

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world ^"%[filename:foo]_^%[filename:bar].png^"
example.jpg=>helloworld.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.141u 0:00.143

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world ^"^%[filename:foo]_%[filename:bar].png^"
example.jpg=>helloworld.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.125u 0:00.126

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world ^"%[filename:foo]_%[filename:bar].png^"
example.jpg=>helloworld.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.125u 0:00.131

C:\>convert example.jpg -verbose -set filename:foo hello -set filename:bar world ^"%[filename:foo]^_%[filename:bar].png^"
example.jpg=>helloworld.png JPEG 352x264 352x264+0+0 8-bit DirectClass 131KB 0.125u 0:00.111

答案1

我无法完全解释您看到的奇怪行为。它几乎看起来像是 cmd.exe 百分比处理的结果,但它与我理解的 cmd.exe 行为并不完全相符。

在批处理文件中转义百分号很容易 - 只需将其加倍即可。但这在命令行中不起作用。从技术上讲,在 Windows cmd.exe 命令行上无法转义百分号。

命令行可以用以下两种方式之一扩展百分比:

1)展开环境变量。

如果两个百分比之间的文本与环境变量的名称匹配,则百分比结构将被值替换。

C:\test>set test=hello

C:\test>echo %test%
hello

如果百分比之间的文本与环境变量名称不匹配,则保留完整的原始文本。

C:\test>echo %notDefined%
%notDefined%

微软自己的文档声称你可以在前面放置一个插入符号来逃避百分比。

C:\test>echo ^%test^%
%test%

但这并不是真正转义百分号。相反,它正在寻找一个名为的变量test^,但找不到。因此原始文本未受干扰。然后在正常转义处理过程中删除插入符号。这可以通过定义名称中带有插入符号的变量来证明。

C:\test>set "test^=goodbye"

C:\test>echo ^%test^%
goodbye

第一个百分号之前的插入符号没有任何作用。您可以在百分号之间的任意位置放置插入符号,只要不存在名称中带有插入符号的变量,它就会阻止变量扩展。

C:\test>echo %te^st%
%test%

如果文本被引用,则不会删除插入符号,除非引号也被转义。

C:\test>echo "%te^st%"
"%te^st%"

C:\test>echo ^"te^st%^"
"%test%"

如果有冒号,情况会稍微复杂一些。变量扩展会使用冒号进行替换和子字符串操作。变量名称是第一个百分号和冒号之间的文本。

C:\test>echo %test:el=a%
halo

C:\test>echo %test:~1,2%
el

如果第一个百分比和冒号之间的文本与变量名不匹配,或者冒号和最后一个百分比之间的文本不是有效的替换或子字符串构造,则保留原始文本而不进行替换。

C:\test>echo %test:1,2%
%test:1,2%

最后这个例子和你的情况很相似。第一个百分号和冒号之间的文本与变量不匹配,而冒号和最后一个百分号之间的文本不是替换或子字符串构造,所以整个建筑应该被保留下来,包括下划线。因此,我不明白 cmd.exe 如何能够删除你的下划线。但在第二个百分号前添加插入符号却保留下划线,这一事实让我感到怀疑。我真的很想知道如果将插入符号移到下划线前会发生什么。


2)扩展 FOR 变量

在您的情况下这不是问题,但如果 FOR 循环有效,百分比也会导致问题。在这种情况下,无法使用插入符号来保护百分比,因为插入符号在 FOR 变量扩展之前被剥离。在此示例中,我希望输出为%A=1,但它不起作用。

C:\test>for %A in (1 2) do @echo ^%^A=%A
1=1
2=2

保护百分比最方便的方法是引入另一个 FOR 变量

C:\test>for %C in (%) do @for %A in (1 2) do @echo %CA=%A
%A=1
%A=2

A将其放入另一个变量中同样有效

C:\test>for %C in (A) do @for %A in (1 2) do @echo %%C=%A
%A=1
%A=2

更新

测试下划线被删除的位置的一个好方法是将 ECHO 放在命令前面。如果原始命令保留了下划线,那么您就知道 convert 实用程序一定是罪魁祸首。

C:\>echo convert example.jpg -verbose -set filename:foo hello -set filename:bar world "%[filename:foo]_%[filename:bar].png"

下划线在我的计算机上保留了:-)


如果你对命令解析器的工作原理感兴趣,请参阅https://stackoverflow.com/a/4095133/1012053。该答案主要涉及批量解析,但规则非常相似,最后的部分简要描述了主要区别。

相关内容