如何避免cmd.exe解释shell特殊字符,如< > ^

时间:2010-09-28 13:45:14

标签: batch-file cmd escaping caret

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

launcher.exe paramX paramY %*

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

launcher.cmd param1 param2 param3 [...]

我遇到的问题是,如果CMD脚本的参数包含< >^等shell特殊字符,则用户必须通过前面的每个字符来强制转义这些字符。插入符^ 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。 / p>

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

2 个答案:

答案 0 :(得分: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&gt; def&amp; geh%ijk | lmn ^ opq!”

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

<强> [编辑/改善] 我创建了另一种检索参数的方法,我认为它也可以接受任何字符串也是你的第二个例子。
甚至像

这样很难的字符串

启动器“^
发射器10 ^>一个 发射器“&amp;”^&amp;

@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语句中(对于REM来说,这不是完全正确的,但这是另一个故事) 好吧,唯一的问题是REM是一个评论并且没有效果:-)

但是如果使用 echo ,还会在执行之前回显rem行(执行rem是一个很好的词)。
下一个问题是显示它,你无法使用正常&gt;重定向此调试输出。 DEBUG.TXT。 如果使用for循环也是如此。

好的,你可以通过像

这样的调用重定向 echo on 输出
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"^

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

答案 1 :(得分: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"

一旦剥离引号,管道符号就会生效。