13

我有一个 Windows CMD 脚本,它接受许多参数并执行一个 EXE,首先传递一些硬编码的参数,然后传递来自用户的所有参数。CMD 脚本如下所示:

launcher.exe paramX paramY %*

用户将从 Windows shell 执行 CMD 脚本,如下所示:

launcher.cmd param1 param2 param3 [...]

我遇到的问题是,如果 CMD 脚本的参数包含 shell 特殊字符,例如< >and ^,则用户被迫通过在每个前面加上 3 个插入符号^shell 转义字符来转义这些字符。

两个例子

1) 要将参数传递ten>one给 EXE,用户必须按如下方式启动 CMD:

launcher.cmd ten^^^>one

这样做的原因是 shell 特殊字符^>命令 shell 在两个级别上解释,第一个在命令行上,第二个在 CMD 脚本内。因此,^必须应用两次使用插入符号 shell 转义字符进行转义的 shell。问题是这对用户来说是不明显的并且看起来很丑陋。

对于此示例,更好的解决方案是用双引号将参数括起来。但是,对于在参数中包含文字双引号的更复杂的示例,这会失效。

2) 要将参数传递"^给 EXE,用户必须按如下方式启动 CMD:

launcher.cmd "\"^^^^"

在我的情况下,我想支持包含任何低 ASCII 字符序列的参数,不包括控制字符,即代码点 0x20 到 0x7E。我知道会有一些例子,用户必须用插入符号转义某些 shell 特殊字符。但是,我希望用户在这些情况下每次都必须使用3 个插入符号,因为他们碰巧调用的是 CMD 脚本而不是 EXE。

我可以通过将 CMD 脚本替换为执行相同操作的 EXE 来解决此问题。但是,有没有办法改变 CMD 脚本,使其参数通过 EXE 而不解释 shell 特殊字符?

4

2 回答 2

8

一种方法是在批次内部使用延迟扩展,因为这样特殊字符就会失去“特殊”含义。

唯一的问题是将参数放入变量中。

像这样的东西可以帮助

@echo off
setlocal DisableDelayedExpansion
rem ** At this point the delayedExpansion should be disabled
rem ** otherwise an exclamation mark in %1 can remove carets
set "param1=%~1"

setlocal EnableDelayedExpansion
rem ** Now you can use the param1, independent of the content, even with carets or quotes
rem ** but be careful with call's, because they start a second round of expansion
echo !param1!
set "tmp=!param1:~1,4!"

现在参数可以用引号括起来,因此不再需要插入符号。示例 launcher.bat "abc>def&geh%ijk|lmn^opq!"

唯一剩下的有问题的特殊字符似乎是引号。

[编辑/改进]我创建了另一种检索参数的方法,我假设它也可以接受任何字符串,也是您的第二个示例。
即使是非常硬的弦,比如

发射器 "^
发射器十^>一个
发射器 "&"^&

@echo off
setlocal DisableDelayedExpansion
set "prompt=X"
for %%a in (1 ) do (
    @echo on
    for %%b in (4) do (
        rem #%1#
    ) 
) > XY.txt
@echo off
for /F "delims=" %%a in (xy.txt) DO (
  set "param=%%a"
)
setlocal EnableDelayedExpansion
set param=!param:~7,-4!
echo param='!param!'

这个怎么运作?
我发现扩展 %1 而不扩展特殊字符(如 " 或 ^备注并且没有效果:-)

但是,如果您在 rem 行上使用 echo,则它们在执行之前也会被回显(为 rem 执行是一个好词)。
下一个问题是它显示出来了,你不能用正常的 > debug.txt重定向这个调试输出。如果您使用 for 循环,这也是正确的。

好的,您可以使用类似的调用重定向输出上 的回声

echo on
call :myFunc > debug.txt

但是如果你调用一个函数,你就不能再访问批处理文件的 %1 了。

但是使用双 for 循环,可以激活调试输出的重定向,并且仍然可以访问 %1。
我将提示更改为“X”,所以我知道它总是只有一个字符长。

剩下的唯一一件事就是解释为什么我在 %1 后面加上 #。
那是因为,在某些情况下,即使在 REM 行中,某些特殊字符也会被识别出来,显然 ;-)

rem This is a remark^
rem This_is_a_multiline^
rem "This is also a multiline"^

所以 # 抑制可能的多行情况。

于 2010-10-06T18:47:39.653 回答
0

这有帮助吗:

EscapPipes.Cmd

@echo off 

:Start
    If [%1]==[] goto :eof
    @Echo %1
    shift
goto :Start

当这样开始时:

EscapPipes.Cmd Andy Pandy "Pudding | > < and pie"  

Andy
Pandy
"Pudding | > < and pie"

只要您去掉引号,管道符号就会生效。

于 2010-09-29T09:51:51.970 回答