在FOR循环中用复杂的调用检索%ERRORLEVEL%

Windowsbatch file中,我需要将程序执行的结果检索到一个variables,当程序调用复杂的空间和几个选项。 经过多次讨论,通过使用CALLfind了一个解决方法:

 FOR /F "delims=" %%G IN ('CALL "C:\path with spaces\foo.bat" "blah blah='foobar' blah"') do set foo=%%G 

请参阅以下问题了解更多细节并了解上下文:

当命令有空格时,将命令输出检索到variables

实际上batch file调用PostgreSQL 9.3,就像这样:

 SET PSQL_EXE=C:\Program Files\Foo\Bar\PostgreSQL\9.3\bin\psql.exe SET FOO=1 REM psql query should result in a 0 or 1 based on the mydbproperty table value FOR /F %%G IN ('call "%PSQL_EXE%" -U pguser -d MyDB -p %PG_PORT% -c "select string_value from mydb.uri as uri, mydb.property as prop where uri.id = prop.property_uriid and uri.namespace_uri='http://example.com/foo/bar/' and uri.simplename = 'fooBar'" -q -t -w') DO SET FOO=%%G REM next line doesn't have ERRORLEVEL set IF !ERRORLEVEL! NEQ 0 EXIT !ERRORLEVEL! 

不幸的是,这种格式似乎导致了一个单独的cmd实例,所以在调用pgsql(例如缺less密码文件)时发生的任何错误都不会被传回,并且%ERRORLEVEL%不会被设置。 具体来说,pgsql打印出来:

 psql: fe_sendauth: no password supplied 

然而%ERRORLEVEL% (和!ERRORLEVEL! )仍然是0

有关更多信息,请参阅以下问题:

批次For循环中的PSQL错误级别

所以现在的问题是如何find%ERRORLEVEL%,现在我已经成功得到了psql的响应。 我不想写一些临时文件—我想在记忆中做所有事情。

(注意,是的,我试图从数据库中查询并存储在FOO将是01 ;不是那么重要,但它确实使事情变得更加混乱)。

我试图通过简单地看到%%G返回的内容来testingAacini提出的解决scheme:

 FOR /F "delims=" %%G IN ('"CMD /V:ON /C CALL "%PSQL_EXE%" -U user -d MyDB -p %PG_PORT% -c "select string_value from mydb.uri as uri, mydb.database_property as prop where uri.id = prop.property_uriid and uri.namespace_uri='http://example.com/foo/bar/' and uri.simplename = 'fooBar'" -q -t -w ^& ECHO !ERRORLEVEL!"') DO ( ECHO %%G ) 

由于psql无法find密码文件,因此会向stderr(按预期方式)输出错误,并且ECHO输出0 —因此ERRORLEVEL不会返回到DO子句。 但是,如果我从FOR循环中分出命令并直接运行它,它会显示ERRORLEVEL2 )就好!

 "%PSQL_EXE%" -U user -d MyDB -p %PG_PORT% -c "select string_value from mydb.uri as uri, mydb.database_property as prop where uri.id = prop.property_uriid and uri.namespace_uri='http://example.com/foo/bar/' and uri.simplename = 'fooBar'" -q -t -w ECHO !ERRORLEVEL! 

更新:如果我已经延迟扩展已经打开,我被告知需要逃脱逃脱,我采取了给定的答案,并更新它考虑到,我打电话的程序将不返回一个值,如果它产生错误。 所以这就是我所做的。 首先我确定两个variables是未定义的:

 SET "var1=" SET "var2=" 

然后我使用@Aacini和@jeb给出的循环,但是在循环内部我这样做:

 if NOT DEFINED var1 ( SET "var1=%%G" ) else ( SET "var2=%%G" ) 

然后在循环之外完成之后:

 if DEFINED var2 ( ECHO received value: !var1! ECHO ERRORLEVEL: !var2! ) ELSE ( ECHO ERRORLEVEL: !var1! ) 

这似乎工作。 (难以置信的是什么体操!)

Solutions Collecting From Web of "在FOR循环中用复杂的调用检索%ERRORLEVEL%"

为了做到这一点,必须结合一些技巧。 放置在for /F集中的批处理文件是在一个新的cmd.exe上下文中执行的,所以我们需要一个方法在这个命令结束时报告它的错误级别。 唯一的方法是通过首先通过显式放置的cmd.exe /C执行所需的批处理文件,然后插入一个报告其错误级别的命令。 但是,放置在for /F集中的命令是在命令行上下文中执行的,而不是在批处理文件上下文中执行的,因此需要在显式cmd.exe中启用延迟扩展( /V:ON开关)。 之后,一个简单的echo !errorlevel! 显示批处理文件返回的错误级别。

 @echo off setlocal set "foo=" FOR /F "delims=" %%G IN ('"CMD /V:ON /C CALL "path with spaces\foo.bat" "blah blah='foobar' blah" ^& echo !errorlevel!"') do ( if not defined foo ( set "foo=%%G" ) else ( set "errlevel=%%G" ) ) echo String output from foo.bat: "%foo%" echo Errorlevel returned by foo.bat: %errlevel% 

这是“路径与空间\ foo.bat”:

 @echo off echo String from foo.bat exit /B 12345 

这是以前代码的输出:

 String output from foo.bat: "String from foo.bat" Errorlevel returned by foo.bat: 12345 

如果在主批处理文件之前已经启用了延迟扩展,则必须使用完全不同的语法。
^!ERRORLEVEL^! 这是必要的,以避免!ERRORLEVEL! 在批处理文件的上下文中执行FOR行之前进行评估。
^^^&是必须带一个^&内部cmd /V:ON表达式

 @echo off setlocal EnableDelayedExpansion set "foo=" REM *** More carets must be used in the next line **** FOR /F "delims=" %%G IN ('"CMD /V:ON /C CALL "path with spaces\foo.bat" "blah blah='foobar' blah" ^^^& echo ^!errorlevel^!"') do ( if not defined foo ( set "foo=%%G" ) else ( set "errlevel=%%G" ) ) 

编辑回复OP的评论

我的第一个解决方案假定程序总是输出一行,所以它从第二行输出错误级别。 但是, 新的规格表明,当程序由于错误而结束时,第一个输出行不会被发送。 修复这个细节很容易,修正后的代码如下所示。

 @echo off setlocal EnableDelayedExpansion set "foo=" FOR /F "delims=" %%G IN ('"CMD /V:ON /C CALL "path with spaces\foo.bat" "blah blah='foobar' blah" ^^^& echo errlevel=^!errorlevel^!"') do ( set "str=%%G" if "!str:~0,8!" equ "errlevel" ( set "errlevel=!str:~9!" ) else ( set "foo=%%G" ) ) echo String output from foo.bat: "%foo%" echo Errorlevel returned by foo.bat: %errlevel% 

但是,原始规范指出执行的程序是一个名为"C:\path with spaces\foo.bat" 的BATCH文件 。 当执行的程序是一个批处理.BAT文件时,这个代码可以正常工作,但是如果执行的程序是一个.exe文件,它就不起作用,正如我在第一个注释中清楚地指出的那样:“注意带有空格的C:\ path \ foo.bat必须是.bat文件!如果这样的文件是一个.exe文件,这个方法不起作用“。 这样,您只需要在执行.exe命令时创建“foo.bat”文件,并在下一行中创建exit /B %errorlevel%命令。 当然,如果你直接通过直接执行.exe程序来测试这个方法,它永远不会工作…

2ND编辑添加了一些解释

这个解决方案的目的是给出一个批处理文件"C:\path with spaces\foo.bat" ,它显示一些输出并返回一个错误级别值,如下所示:

 @echo off echo String from foo.bat exit /B 12345 

…可能会被另一个同时获得输出和错误级别的批处理文件调用。 这可以用一个非常简单的方法使用一个辅助文件完成:

 @echo off call "C:\path with spaces\foo.bat" > foo.txt set errlevel=%errorlevel% set /P "foo=" < foo.txt echo String output from foo.bat: "%foo%" echo Errorlevel returned by foo.bat: %errlevel% 

但是,这个请求表明:“不要写入一些临时文件—我想在内存中做所有事情”。

从另一个命令获取输出的方式是通过FOR /F ,如下所示:

 FOR /F "delims=" %%G in ('CALL "C:\path with spaces\foo.bat" "blah blah='foobar' blah"') do set foo=%%G 

但是,放置在FOR / F集中的批处理文件在新的cmd.exe上下文中执行。 更确切地说,前面FOR命令中foo.bat的执行与下一个代码完全等价

 CMD /C CALL "C:\path with spaces\foo.bat" "blah blah='foobar' blah" > tempFile & TYPE tempFile & DEL tempFile 

tempFile每行分配给%%G可替换参数,并执行set foo=%%G命令。 请注意,上一行的执行方式与在命令提示符( 命令行上下文 )中输入一样,因此有几个命令在此上下文中不起作用(如转到/调用标签,setlocal / endlocal等)以及延迟扩展设置为cmd.exe的初始值,通常是禁用的。

为了简化下面的描述,我们使用一个较短但相似的FOR /F命令,该命令foo.bat下面foo.bat执行的等效内部代码一起显示:

 FOR /F %%G in ('CALL "foo.bat"') do set foo=%%G -> CMD /C CALL "foo.bat" <- we also omit the "tempFile" parts 

这样,当这样的命令结束时,我们需要一个方法来报告foo.bat 。 唯一的方法是通过首先通过显式放置的cmd.exe /C执行所需的批处理文件,然后插入一个报告其错误级别的命令。 那是:

 FOR /F %%G IN ('"CMD /C CALL foo.bat ^& echo !errorlevel!"') do set foo=%%G -> CMD /C "CMD /C CALL foo.bat ^& echo !errorlevel!" 

请注意,&号必须以这种方式转义: ^& ; 否则会错误地分割放置在FOR /F命令集中的命令。 在前一行中, foo.batecho !errorlevel! 命令通过嵌套的CMD /C

但是,放置在FOR / F集中的命令是在命令行上下文中执行的,因此需要在显式cmd.exe中启用延迟扩展( /V:ON开关)。 否则, echo !errorlevel! 只是不工作:

 FOR /F %%G IN ('"CMD /V:ON /C CALL foo.bat ^& echo !errorlevel!"') do set foo=%%G -> CMD /C "CMD /V:ON /C CALL foo.bat ^& echo !errorlevel!" 

好。 请注意,前面的描述假设执行FOR / F命令时,延迟扩展是DISABLED。 如果启用了什么更改? 1.错误 !errorlevel! 被当前的errorlevel值替换,并且2.用于转义字符的脱字符被删除。 执行FOR / F命令之前分析行时完成这些更改。 让我们看看这个细节:

 SETLOCAL ENABLEDELAYEDEXPANSION FOR /F %%G IN ('"CMD /V:ON /C CALL foo.bat ^& echo !errorlevel!"') do set foo=%%G -> After parsed: FOR /F %%G IN ('"CMD /V:ON /C CALL foo.bat & echo 0"') do set foo=%%G 

上一行发出错误,因为非转义的&像往常一样。 我们需要保留惊叹号和和号的插入符号。 保留惊叹号很容易: ^!errorlevel^! ,但是我们如何保存^& ? 如果我们使用^^&第一个插入符号保留第二个符号,但是下一个要解析的字符是&孤独的,并且出现通常的错误! 所以,正确的方法是: ^^^& ; 第一个字符保存第二个字母并给出^ ,并保留&符号:

 SETLOCAL ENABLEDELAYEDEXPANSION FOR /F %%G IN ('"CMD /V:ON /C CALL foo.bat ^^^& echo ^!errorlevel^!"') do set foo=%%G -> After parsed: FOR /F %%G IN ('"CMD /V:ON /C CALL foo.bat ^& echo !errorlevel!"') do set foo=%%G