难以在批处理文件

时间:2016-01-15 20:07:27

标签: batch-file

我正在尝试从另一个调用一个bat文件,同时将目标变量保持为本地(主要是)。下面的代码是我失败的尝试,我想要的var缺失,但本地仍然存在。 我找到了一些关于使用setlocal here的信息,这就是我认为我拥有它的方式。此外,我用来推动var过去setlocal的信息是here。我很可能错过了这些东西。任何帮助表示赞赏!

呼叫蝙蝠:

@SET errmsg=Old Message
@CALL Target.bat TEST_JOB errmsg
@ECHO.jobname = %jobname%, but should be undefined
@IF DEFINED errmsg ECHO.Error occurred: %ERRORLEVEL% %errmsg%

目标蝙蝠:

@SETLOCAL
@ECHO OFF

:START
SET jobname=%~1
SET errmsgvar=%~2

:MAIN
CALL:ERROR "New Error Message"
ECHO.Should have never returned here
ENDLOCAL

:EXIT
@ECHO ON
@EXIT /b %ERRORLEVEL%

:ERROR "errorMesage"
ENDLOCAL & SET "%errmsgvar%=%~1"
ECHO.%errmsg% ^<-- Testing only, we don't know what the actual var will be
(GOTO) 2>NUL & GOTO:EXIT

结果:

C:\Projects\Batch>Caller.bat
New Error Message <-- Testing only, we don't know what the actual var will be
jobname = TEST_JOB, but should be undefined
Error occurred: 0 Old Message

为了清晰起见进行编辑:

我想在这里完成几件事......

  • 允许调用者指定错误消息应存储在哪个var中第二次参加座谈会
  • 立即错误退出,将错误消息保存到提供的var | :ERROR阻止
  • 将所有变量保留在目标本地,通过在endlocal line上设置推送错误消息

看起来我的endlocal被忽略了,它只是在文件的末尾做了endlocal,这打破了事情

4 个答案:

答案 0 :(得分:2)

正如Magoo所说,您的TEST_JOB值已正确清除。您的测试必须查看先前运行的保持结果。最好在CALL之前明确地清除你的呼叫球中的值,这样你就永远不会得到错误的结果..

由于你的逻辑缺陷,你的errmsg没有被设置。在我对(goto) 2>nul提供一些介绍之后,再详细说明。

您正在尝试使用未广为人知的相对较新的(goto) 2>nul技术。它实际上是exit /b,除了附加的连接命令仍然执行,但在调用者的上下文中。我相信第一个发现发表在http://forum.script-coding.com/viewtopic.php?id=9050(我无法阅读),第一个已发布的英文帖子是http://www.dostips.com/forum/viewtopic.php?f=3&t=6491

自DosTips发布以来,已经产生了许多有用的工具:

在您的代码中,您尝试在错误例程中使用ENDLOCAL,但这只会影响在例程中发出的SETLOCAL。您需要使用(goto) 2>nul技巧之前来发布ENDLOCAL,以便ENDLOCAL在父上下文中正常工作。

:error "errormesage"
(
  (goto) 2>nul
  endlocal
  set "%errmsgvar%=%~1"
  goto :exit
)

我担心你的退回ERRORLEVEL。当发生错误时,我不认为它会返回您想要的值。

您必须记住,所有外部命令始终设置ERRORLEVEL是否存在错误。几乎所有内部​​命令在出错时都将ERRORLEVEL设置为非零,并且一些内部命令在成功时将ERRORLEVEL设置为0(但如果没有错误,则有些保留现有的ERRORLEVEL)。

最安全的做法是将您想要的错误号显式传递给:error例程,然后在退出时显式设置该值。这是一种方法:

:error "errMsg" errNum
(
  (goto) 2>nul
  endlocal
  set "%errmsgvar%=%~1"
  cmd /c exit %2
  goto :exit
)

但是我会修改一些内容,这样无论是否有错误,你都会以同样的方式退出。如果出现错误,则调用下面的退出例程,并显示错误消息和错误号,并正确设置值。正常退出时,两个值都没有传入,因此errmsg变量被清除,例程返回当前的ERRORLEVEL(大概为0)。

调用脚本

@setlocal
@set errmsg=Old Message
@set "jobname="
@call target.bat TEST_JOB errmsg
@if defined jobname (
  echo ERROR - jobname = %jobname%
) else (
  echo SUCCESS - jobname is undefined
)
@if defined errmsg (
  echo.Error occurred: %errorlevel% %errmsg%
) else (
  echo No error occurred
)

<强> Target.bat

@echo off
setlocal enableDelayedExpansion

:start
set jobname=%~1
set errmsgvar=%~2

:main
set /a "1/(%random% %% 3)" || call :exit !errorlevel! "Random error"
echo Success
call :exit


:exit  [errNum]  ["errMsg"]
(
  (goto) 2>nul
  endlocal
  set "%errmsgvar%=%~2"
  echo on
  exit /b %1
)

:主代码在尝试除以零时随机增加33%的错误。

以下是一些示例输出,显示了两种可能的结果:

C:\test>test
Success
SUCCESS - jobname is undefined
No error occurred

C:\test>test
Divide by zero error.
SUCCESS - jobname is undefined
Error occurred: 1073750993 Random error

答案 1 :(得分:1)

完美地为我工作。

我相信您的测试集jobname在某个阶段,因此您的setlocal/endlocal帧只会恢复原始环境。

我建议您执行

set "jobname="

作为调用蝙蝠的第一行,以便在操作开始前强制jobname清除。

答案 2 :(得分:0)

将第二行改为

ECHO.%errmsgvar%.....

匹配第3行到最后一行

作为解释......     ENDLOCAL&amp; SET“errmsgvar =%~1” 1.加载整行并且扩展%~1。 2.执行ENDLOCAL并结束本地化。 3. SET“errmsgvar =%~1”执行为......    SET“errmsgvar = WhateverWasIn%~1” 这种技术是隧道技术,是一种跨区域传递变量的方法。

答案 3 :(得分:0)

将您的第二个脚本更改为以下内容。如果在脚本末尾占用ENDLOCAL。我相信ENDLOCAL块中的:ERROR仅适用于该块,而不是整个文件。

@SETLOCAL
@ECHO OFF

:START
SET jobname=%~1
SET errmsgvar=%~2

:MAIN
CALL:ERROR "New Error Message"
ECHO.Should have never returned here
ENDLOCAL

:EXIT
REM case insensitive test for errmsgvar.
IF DEFINED errmsgvar (
    IF /I NOT "%errmsgvar%" == "" (
        ENDLOCAL & SET "errmsg=%errmsg%"
    )
)
@ECHO ON
@EXIT /b %ERRORLEVEL%

:ERROR "errorMesage"
ENDLOCAL & SET "errmsgvar=%~1"
SET "errmsg=%errmsgvar%"
ECHO.%errmsg% ^<-- Testing only, we don't know what the actual var will be
(GOTO) 2>NUL & GOTO:EXIT

输出变为(为清晰起见,额外的换行符):

Caller.bat

New Error Message <-- Testing only, we don't know what the actual var will be

jobname = TEST_JOB

Error occurred: 0 New Error Message