批处理 - 使用垂直条(“|”)在脚本内调用脚本会导致错误

时间:2018-06-13 21:22:31

标签: batch-file cmd scripting windows-scripting

如果脚本在调用EXIT 1时不在关闭终端的情况下退出子程序。我使用this if再次调用脚本。

这个工作正常,直到我现在发现一个带引号的竖线作为参数"!"的问题。我收到一条错误,指出该命令拼写错误。

以下是脚本失败的部分:

@ECHO OFF
    SETLOCAL DISABLEDELAYEDEXPANSION

    IF "%selfWrapped%"=="" (
        REM this is necessary so that we can use "exit" to terminate the batch file,
        REM and all subroutines, but not the original cmd.exe
        SET selfWrapped=true
        %ComSpec% /s /c ""%~0" %*"
        GOTO :EOF
    ) 

    echo %*

    ENDLOCAL
EXIT /B 0

呼叫:

test.cmd "hello world" "|"

预期产出:

"hello world" "|"

我在%*中检查了IF的值,但是使用竖线以及任何其他引用的字符串似乎完全合法。

因此...

  1. 为什么脚本会失败?
  2. 我该如何解决?

3 个答案:

答案 0 :(得分:3)

我不同意链接中的一些描述。 请参阅exit /?准确的帮助说明。

  • exit退出翻译。
  • exit 1使用exitcode 1退出解释器。
  • exit /b具有与退出的goto :eof类似的行为 脚本或被叫标签。 Errorlevel 未重置,因此允许 errorlevel 从上一个命令可以访问之后 退出脚本或被叫标签。
  • exit /b 1使用 errorlevel 1 退出脚本或被叫标签。

如果您在CMD提示符处奇怪地使用exit /b,它将退出解释器。

主要代码:

@ECHO OFF
    SETLOCAL DISABLEDELAYEDEXPANSION

    SET args=%*
    SET "self=%~f0"

    IF "%selfWrapped%"=="" (
        @REM this is necessary so that we can use "exit" to terminate the batch file,
        @REM and all subroutines, but not the original cmd.exe
        SET "selfWrapped=true"

        SETLOCAL ENABLEDELAYEDEXPANSION
        ECHO !ComSpec! /s /c ""!self!" !args!"
        "!ComSpec!" /s /c ""!self!" !args!"
        GOTO :EOF
    )

    ECHO(%*
EXIT /B 0

使用GOTO :EOFEXIT /B 0都会退出脚本。 脚本退出时隐含ENDLOCAL。 显式使用ENDLOCAL是为了结束时间 当前本地范围并继续脚本。一如既往地存在 所有时间都是明确的选择。

%*设置为args会使双引号保持配对。 引用即set "args=%*"有时会引起问题 虽然不使用引号允许代码注入,即 参数"arg1" ^& del *.*。如果del *.*没有进展 要在set行执行,那么它可能会发生 在ComSpec行。对于这个例子,我选择不引用。 所以,这是一个选择。

您在脚本开始时使用禁用扩展。那 保存好的!参数。在你执行之前 ComSpec但是,启用延迟展开并使用!args! 现在不受翻译的保护现在没有看到| 或任何其他可能引发错误的特殊字符。

您的脚本在|参数公开时失败。

C:\Windows\system32\cmd.exe /s /c ""test.cmd" "  | ""

上面回应了对ComSpec行的评价 设置@ECHO ON。注意引号的配对 即""" """。注意插入的额外间距 解释器不考虑的|字符周围 它作为引用字符串的一部分。

与回显评估的更新代码更改相比......:

"!ComSpec!" /s /c ""!self!" !args!"

引号之间的字符串保持不变。没有额外的间距 插入字符串。回应的评估看起来很好 执行得很好。

<强> 声明:

表达CMD的运作就像走紧绳索一样。 就在你认为自己知道的时候,脱掉绳子。

答案 1 :(得分:2)

我根本没有看到将参数附加到%ComSpec% /s /c ""%~0" %*"的必要性。

由于您已经使用变量(selfWrapped)来检测,如果需要包装调用,您还可以将参数放入变量中。

set args=%*

然后您可以在您的子实例中使用!args!

@ECHO OFF
    setlocal DisableDelayedExpansion

    IF "%selfWrapped%"=="" (
        @REM this is necessary so that we can use "exit" to terminate the batch file,
        @REM and all subroutines, but not the original cmd.exe
        SET "selfWrapped=true"

        SET ^"args=%*"
        "%ComSpec%" /s /c ""%~f0""
        GOTO :EOF
    )

:Main

    setlocal EnableDelayedExpansion
    ECHO(!args!
EXIT /B 0

现在唯一的问题是set args=%* 如果您无法控制内容,则无法以简单安全的方式访问%*
想想这批次调用

  

myBatch.bat&#34; abc |&#34;
  myBatch.bat abc ^ |
  myBatch.bat abc ^ | - &#34; |&#34;

但你可以使用How to receive even the strangest command line parameters?
Get arguments without temporary file

顺便说一下。您可以免除您的子进程,也可以退出功能
Exit batch script from inside a function

答案 2 :(得分:1)

对上述答案进行一次修正。 是的,脚本末尾隐含ENDLOCAL,但有一个问题。

我发现使用嵌套脚本时,如果您在ENDLOCAL之前没有EXIT /B 1,则不会在下一级获得1的返回码出脚本。

如果您只有EXIT /B 0,那么这无关紧要,因为默认返回代码为0