Batch file 批处理-在带有竖条(";&124quot;)的脚本中调用脚本会导致错误

Batch file 批处理-在带有竖条(";&124quot;)的脚本中调用脚本会导致错误,batch-file,cmd,scripting,windows-scripting,Batch File,Cmd,Scripting,Windows Scripting,如果在调用退出1时,脚本应在子例程中退出而不关闭终端。这里我使用if再次调用脚本 这很好,直到我现在发现一个带有引号的竖条作为参数的问题。“!”。我收到一个错误,说明命令拼写错误 以下是脚本中失败的部分: @ECHO OFF SETLOCAL DISABLEDELAYEDEXPANSION IF "%selfWrapped%"=="" ( REM this is necessary so that we can use "exit" to terminate t

如果在调用
退出1
时,脚本应在子例程中退出而不关闭终端。这里我使用
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
电话:

预期产出:

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

所以

  • 为什么脚本会失败
  • 我怎样才能修好它

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

    • exit
      退出解释器
    • exit 1
      使用exitcode 1退出解释器
    • exit/b
      的行为与退出的
      goto:eof
      类似 该脚本或称为标签。Errorlevel未重置,因此允许 可在之后访问上一个命令的errorlevel 退出脚本或被调用的标签
    • exit/b1
      以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:EOF
    EXIT/b0
    都将退出脚本。
    ENDLOCAL
    隐含在脚本的出口处。 显式使用
    ENDLOCAL
    用于您希望结束 当前本地作用域并继续脚本。像往常一样 始终明确是一种选择

    %*
    设置为
    args
    可使双引号保持成对。 引用,即
    设置“args=%*”
    有时会导致问题 虽然不使用引号,但允许代码注入,即。 参数
    “arg1”^&del*
    。如果
    del*
    没有运行 要在
    set
    行执行,则可能会发生这种情况 在
    ComSpec
    行。对于这个例子,我选择了不引用。 因此,这是一种选择

    您正在脚本开始时使用禁用的扩展。那个 保存
    参数,这是好的。在你执行之前
    
    ComSpec
    但是,启用延迟扩展并使用
    !阿格斯
    它现在受到保护,不会被解释器看到
    |
    或任何其他可能引发错误的特殊字符

    |
    参数暴露时,脚本失败

    C:\Windows\system32\cmd.exe /s /c ""test.cmd" "  | ""
    
    以上是对
    ComSpec
    行的重复评估 设置
    @ECHO ON
    。注意引号的配对 i、 e.
    。请注意插入的额外间距
    绕过<代码> <代码>字符,因为解释程序不考虑
    它作为带引号的字符串的一部分

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

    "!ComSpec!" /s /c ""!self!" !args!"
    
    引号之间的字符串保持不变。没有额外的间距 插入到字符串中。回响的评估看起来很好,而且 执行良好

    免责声明:

    @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
    
    表达CMD的工作方式就像走钢丝一样。
    就在你认为你知道的时候,从绳子上摔下来。

    我认为没有必要将参数附加到你的
    %ComSpec%/s/c”“%~0”%*“

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

    set 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
    
    现在剩下的唯一问题是
    设置参数=%*

    如果无法控制内容,则无法以简单安全的方式访问
    %*

    想想这个批处理调用

    myBatch.bat“abc|”
    myBatch.bat abc^|
    myBatch.bat abc^|--“|”

    但是你可以使用

    顺便说一句,您可以腾出子进程,也可以退出函数

    查看对上述答案的一处更正。 是的,
    ENDLOCAL
    隐含在脚本末尾,但是有一个陷阱

    我发现,对于嵌套脚本,如果在退出/b1之前不执行
    ENDLOCAL
    ,则在下一级脚本中将无法获得
    1
    的返回代码


    如果您只退出/b0
    ,那么这并不重要,因为默认返回代码是
    0

    如果您直接运行bat或使用call:
    “%~0”%*
    调用“%~0”%*
    可以吗:
    %ComSpec%”/S/C“%~0”%*
    ?@Squashman,如备注中所述,新的
    cmd
    实例需要能够使用
    exit
    终止批处理脚本及其所有子例程,因此,当前实例不会变为quit…这是怎么回事:
    %ComSpec%”/S/C^^^^“%~0”%*^^^^“
    ,甚至是
    %ComSpec%”/S/C^^^^^“%~0“%
    ?看看Jeb和Dbenham对这个问题的看法会很有趣。他们对解析规则非常了解。不错,但是考虑到延迟扩展只在父
    cmd
    实例中有帮助,而在子实例中没有帮助!无论如何,仍有改进的余地:1。当启用延迟扩展时,您正在扩展
    %
    -变量,这可能会导致问题,尤其是
    的问题字符;将所有
    %
    扩展更改为
    要安全的代码(如
    !ComSpec!
    ;然后在启用延迟扩展之前执行
    设置“self=%~0F”
    ,并使用
    !self!
    而不是以后使用
    %~f0
    )。2.放
    !通信规范
    介于
    之间“
    允许自定义系统目录包含空格。3.使用安全的
    echo
    语法
    echo(%*
    .4.I