获取%ERRORLEVEL%在FOR循环中调用复杂 [英] Retrieve %ERRORLEVEL% with complicated call in FOR loop

查看:489
本文介绍了获取%ERRORLEVEL%在FOR循环中调用复杂的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Windows批处理文件我需要检索程序执行到一个变量,当程序调用用的空间和几种选择复杂的结果。经过多次讨论,解决方法是使用呼叫找到:

In a Windows batch file I needed to retrieve the result of a program execution into a variable, when the program call was complicated with spaces and several options. After much discussion, a workaround was found by using CALL:

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

请参阅以下问题了解更多详情,并了解情况:

Please see the following question for more details and to understand the context:

检索命令输出变量时,命令中有空格

在现实中的批处理文件调用的PostgreSQL 9.3,是这样的:

In reality the batch file calls PostgreSQL 9.3, like this:

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(如缺少密码文件),就不会获得通过背部和%ERRORLEVEL%不被设置。具体的pgsql打印出来:

Unfortunately it appears that this format results in a separate cmd instance, so any error that occurred in calling pgsql (e.g. the lack of a password file) does not get passed back and the %ERRORLEVEL% does not get set. Specifically pgsql prints out:

psql: fe_sendauth: no password supplied

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

请参阅以下问题的详细信息:

See the following question for more info:

在批次循环 PSQL错误级别

所以,现在的问题是如何找出%ERRORLEVEL%,现在我在获得的psql 的反应已经取得了成功。我想preFER不要写一些临时文件---我想做的事情在内存中的一切。

So now the question is how to find out the %ERRORLEVEL%, now that I've succeeding in getting the response of psql. I'd prefer not to write to some temporary file---I want to do everything in memory.

(注意,是的,我想从数据库和存储查询值 FOO 将是 0 1 ;这不是问题,但它确实使事情变得更加混乱)

(Note that, yes, the value I'm trying to query from the database and store in FOO will be either a 0 or a 1; not that it matters, but it does make things more confusing.)

我试图通过简单地看到的会是什么 %%被G返回测试Aacini的提出的解决方案

I tried to test Aacini's proposed solution by simply seeing what would be returned in %%G:

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找不到密码文件,这种打印错误到std​​err(预期)和 ECHO 输出 0 ---所以 ERRORLEVEL 不获取发送回 DO 条款。但是,如果我从循环打出了命令,只是直接运行它,它显示了 ERRORLEVEL 2 )就好了!

Because psql could not find a password file, this prints an error to stderr (as expected) and the ECHO outputs 0---so the ERRORLEVEL is not getting sent back to the DO clause. But if I split out the command from the FOR loop and just run it directly, it shows the ERRORLEVEL (2) just fine!

"%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!

更新:被建议之后,我需要逃跑的逃跑,如果我已经推迟扩张已经打开,我把给定的答案和更新它考虑到,我打电话程序将不会返回一个值它会产生一个错误。因此,这里是我做什么;首先,我要确保两个变量是不确定的:

Update: After being advised that I need to escape the escapes if I have delayed expansion already turned on, I took the given answer and updated it to take into account that the program I'm calling won't return a value if it generates an error. So here's what I do; first I make sure two variables are undefined:

SET "var1="
SET "var2="

然后我使用@Aacini和@jeb给循环,但在循环里我这样做:

Then I use the loop that @Aacini and @jeb gave, but inside the loop I do this:

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

然后循环完成任务后外:

Then outside the loop after it has finished:

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

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

That seems to work. (Unbelievably---what gymnastics!)

推荐答案

一些技巧,必须在为了做到这一点相结合。放在 FOR / F 设置批处理文件是在一个新的CMD.EXE环境中执行的,所以我们需要一个方法来报告时,其ERRORLEVEL这样的命令结束。要做到这一点的唯一方法是先通过放置的cmd.exe / c 明确,并执行所需的批处理文件的之后的插入命令该报告的错误级别。然而,摆在 FOR / F 设置命令在的命令行环境中执行的,而不是在批处理文件上下文,因此它是需要启用延迟扩展:在明确的cmd.exe( / V ON 开关)。在此之后,一个简单的回音!错误级别!显示的批处理文件返回的错误级别。

A couple tricks must be combined in order to do that. The Batch file placed in the for /F set is executed in a new cmd.exe context, so we need a method to report its errorlevel when such command ends. The only way to do that is by first executing the desired Batch file via a cmd.exe /C placed explicitly, and after it insert a command that report its errorlevel. However, the commands placed in the for /F set are executed in the command-line context, not in the Batch file context, so it is necessary to enable delayed expansion (/V:ON switch) in the explicit cmd.exe. After that, a simple echo !errorlevel! show the errorlevel returned by the Batch file.

@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

This is "path with spaces\foo.bat":

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

这是previous code的输出:

And this is the output of previous code:

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

如果延迟扩展已经启用之前在主批文件,则相当不同的语法都可以使用。结果
^!^ ERRORLEVEL!这是必须避免的,该!ERRORLEVEL!在批处理文件中的环境中计算,该线路将被执行了。结果
^ ^ ^&安培; 要带一个 ^&安培; 在内 CMD / V:ON 前pression

If delayed expansion is already enabled before in the main batch file then a quite different syntax have to be used.
^!ERRORLEVEL^! This is necessary to avoid, that the !ERRORLEVEL! is evaluated in the context of the batch file, before the FOR line will be executed.
^^^& is necessary to bring a single ^& to the inner cmd /V:ON expression

@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的评论

我的第一个解决方案的假设的,该方案一直输出一条线,所以它采取了错误级别从第二输出线。然而,在新规范的指示该程序结束时,因为错误时,第一输出线不被发送。这是很容易解决这个问题的细节,并修正code如下。

My first solution assumes that the program always output one line, so it took the errorlevel from the second output line. However, the new specification indicate that when the program ends because an error, the first output line is not sent. It is very easy to fix this detail, and the corrected code is below.

@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:用空格\\路径\\ foo.bat 。这code正确工作时,执行程序的要求批处理.bat文件,但如果执行的程序是一个.exe文件,这是行不通的,因为我在我的第一个评论明确指出:需要注意的是C:\\用空格\\ foo.bat路径必须是.bat文件!这个方法不工作,如果这样的文件是一个.exe。通过这种方式,你只需要与.exe文件的下一行的执行和退出/ B%ERRORLEVEL%命令创建foo.bat文件。当然,如果你直接直接执行.exe程序测试此方法,它永远不会工作...

However, the original specification indicate that the executed program is a BATCH file called "C:\path with spaces\foo.bat". This code correctly works when the executed program is a Batch .BAT file as requested, but it does not work if the executed program is an .exe file, as I clearly indicated in my first comment: "Note that C:\path with spaces\foo.bat must be a .bat file! This method don't works if such file is an .exe". This way, you just need to create the "foo.bat" file with the execution of the .exe and an exit /B %errorlevel% command in the next line. Of course, if you directly test this method by directly executing the .exe program, it never will work...

2ND修改补充几点说明

该解决方案的目的是,给予一个批处理文件C:用空格\\ foo.bat \\路径,显示一些输出,并返回一个错误级别值,例如这一个:

The purpose of this solution is that giving a Batch file "C:\path with spaces\foo.bat" that show some output and return an errorlevel value, like this one:

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

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

... it may be called by another Batch file that get both the output and the errorlevel. This can be done in a very easy way using an auxiliary file:

@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%

但是,请求指示:不要写一些临时文件---我想在内存中做的一切

However, the request indicate: "not to write to some temporary file---I want to do everything in memory".

得到另一个命令输出的方式是通过 FOR / F ,就像这样:

The way to get the output from another command is via FOR /F, like this:

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

然而,摆在FOR / F设定批处理文件是在一个新的CMD.EXE上下文中执行。为了更precise, foo.bat 在previous FOR命令的执行是的完全等价的的下一个code:

However, The Batch file placed in the FOR /F set is executed in a new cmd.exe context. To be more precise, the execution of foo.bat in previous FOR command is entirely equivalent to the next code:

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

临时文件分配给每个行 %%摹替换参数和集富= %%摹命令被执行。需要注意的是previous行,如果它是在命令提示符(命令行上下文的),所以几个命令不会在这方面的工作(如跳转进入执行/调用一个标签,SETLOCAL / ENDLOCAL等),以及延迟扩展被设定为cmd.exe初始值,通常被禁用。

Each line in tempFile is assigned to the %%G replaceable parameter and the set foo=%%G command is executed. Note that previous line is executed as if it was entered at the command-prompt (command-line context), so several commands does not work in this context (like goto/call to a label, setlocal/endlocal, etc) and delayed expansion is set to the initial value of cmd.exe, usually disabled.

为了简化以下的说明中,我们使用一个更短的,但类似的 FOR / F 命令时显示与执行的等效内部code foo.bat 下面的:

In order to simplify the following description, we use a shorter, but similar FOR /F command that is shown with the equivalent internal code of the execution of foo.bat below it:

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 明确,并执行所需的批处理文件的之后的插入命令该报告的错误级别。这就是:

This way, we need a method to report the errorlevel of foo.bat when such command ends. The only way to do that is by first executing the desired Batch file via a cmd.exe /C placed explicitly, and after it insert a command that report its errorlevel. That is:

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 命令的命令。在previous线均 foo.bat 回音!错误级别!命令通过嵌套执行 CMD / C

Note that the ampersand sign must be escaped this way: ^&; otherwise it will incorrectly split the commands placed in the set of the FOR /F command. In previous line both the foo.bat and the echo !errorlevel! command are executed via the nested CMD /C.

然而,摆在FOR / F组的命令在命令行环境中执行如前所述,因此有必要使延迟扩展( / V:ON 开关)中明确CMD.EXE;否则,回声错误级别只是没有作品:!

However, the commands placed in the FOR /F set are executed in the command-line context as explained before, so it is necessary to enable delayed expansion (/V:ON switch) in the explicit cmd.exe; otherwise, the echo !errorlevel! just not works:

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命令时所承担的previous说明了推迟扩张是禁用的。如果启用了哪些变化? 1 !ERRORLEVEL!被当前ERRORLEVEL值代替,和 2 用于任何插入符转义字符被删除。当行被解析这些变化完成后,的的执行FOR / F命令。让我们看到了这一点的细节:


Ok. Note that the previous description assumed that delayed expansion was DISABLED when the FOR /F command is executed. What are the changes if it was enabled? 1. The !errorlevel! is replaced by the current errorlevel value, and 2. Any caret that is used to escape a character is removed. These changes are done when the line is parsed, before the FOR /F command is executed. Lets see this point with detail:

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

previous网上发布一个错误,因为转义&安培; ,一如往常。我们需要&符preserve两个感叹号和的的。 preserve感叹号很简单:! ^错误级别^ ,而是我们如何preserve在插入符号^&安培; ?如果我们使用 ^^&安培; 首插入符号preserve第二个,但下一个字符解析为&安培; 单独和平常的错误出现!所以,正确的做法是: ^ ^ ^&安培; ;第一个插入符号preserve第二个,给 ^ ^&安培; preserve的符号:

Previous line issue an error because the unescaped &, as usual. We need to preserve both the exclamation marks and the caret of the ampersand. Preserve exclamation marks is easy: ^!errorlevel^!, but how we preserve the caret in ^&? If we use ^^& the first caret preserve the second one, but the next character to parse is & alone and the usual error appears! So, the right way is: ^^^&; the first caret preserve the second one and give ^, and the ^& preserve the ampersand:

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

这篇关于获取%ERRORLEVEL%在FOR循环中调用复杂的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆