批处理 - 如何在脚本中读取另一个命令行函数的输出?

3

我对批处理脚本还不是很熟悉,但之前做过一些Bash脚本。我的批处理文件将运行另一个程序(即phpunit),这个程序将向命令提示符输出一些内容。我该如何在批处理脚本中截取这些输出并根据其做出决策?例如,phpunit通常会为每个成功运行的测试打印“.”,而为每个失败的测试打印“F”。我该如何让批处理脚本捕获这些输出并根据它是否看到“.”或“F”来执行不同的操作?

3个回答

6
我将不回答你实际的问题,但以下建议可能对解决你尝试解决的潜在问题有用:
  1. 您可以让PHPUnit以机器易读的方式记录结果
  2. PHP是编写命令行脚本的有效替代方案。我使用BASH处理简单的事情,但一旦超出了几行或者当我想要添加循环、if语句等时,我决定使用PHP进行适当处理。其他人可能会选择perl或python。

在你的特定情况下,我会使用PHP而不是BASH,因为它最终可能需要进行一些复杂的解析。但是,我们先看看如何在BASH中完成简单的操作。挑战是输出可能看起来像这样:

PHPUnit 3.4.5 by Sebastian Bergmann.

......................................

Time: 10 seconds, Memory: 9.00Mb

OK (38 tests, 660 assertions)

或者可能看起来像这样:

PHPUnit 3.4.5 by Sebastian Bergmann.

............................................................  60 / 380
............................................................ 120 / 380
...............................................S............ 180 / 380
.......S.................................................... 240 / 380
............................................................ 300 / 380
............................................................ 360 / 380
....................

Time: 01:44, Memory: 14.50Mb

OK, but incomplete or skipped tests!
Tests: 380, Assertions: 6546, Skipped: 2.

或者可能看起来像这样:

PHPUnit 3.4.5 by Sebastian Bergmann.

..IF

Time: 0 seconds, Memory: 8.00Mb

There was 1 failure:

1) MyTest::testTemp
Failed asserting that <boolean:false> is true.

/path/to/myTest.php:68

FAILURES!
Tests: 4, Assertions: 5, Failures: 1, Incomplete: 1.

我猜你的应用程序类似于每小时运行所有测试并确保没有任何故障的定时Cron作业。因此,我将使用正则表达式来搜索“FAILURES”一词:(请参考)

#!/bin/bash

RESULT=`phpunit tests/myTest.php`

if [[ $RESULT =~ FAILURES ]]
then
    echo "Excuse me, Sir, but we have a problem in the unit tests...";echo "$RESULT"
fi

我使用反引号来捕获输出。一些BASH专家会告诉你要使用$()代替。不过这两种方式都可以使用:

....
RESULT=$(phpunit tests/myTest.php)
...

1

如果有其他人正在寻找相同的解决方案,可以通过以下方式实现:

setlocal
someprog.exe > temp.txt
set /p PROGOUTPUT= < temp.txt
del temp.txt
REM do something with %PROGOUTPUT% here
endlocal

希望这能有所帮助。
编辑: 我还想指出,虽然您可以使用上面的方法,但我最终使用了PHP来完成类似于Darren Cook下面建议的操作。

你在那里做的事情和我在示例中展示的使用反斜杠有什么区别吗?我认为获取输出是微不足道的部分,“做某事”在你上面的代码片段中才是真正的挑战! - Darren Cook
是的,“做某事”部分肯定是更难的部分,这就是我在 PHP 中使用类似“exec('phpunit sometest', $output)”而不是 bash 的原因。 - golmschenk

0
关于你的解决方案,有一点需要注意:如果someprog.exe生成了空输出(无论是什么都没有还是只有一个空行),SET/ P语句将不会更改PROGOUTPUT的值,这意味着变量将保留其先前的值(如果有)。因此,如果该代码片段可能在脚本的单个执行中运行多次,则您可能需要确保获取的输出与最近一次调用someprog相关,而不是上一次的调用。
除此之外,你的代码很好,但借助FOR /F循环可以避免使用临时文件:
SETLOCAL
SET PROGOUTPUT=
FOR /F "delims=" %%L IN ('someprog.exe') DO SET "PROGOUTPUT=%%L"
REM do something with %PROGOUTPUT% here
ENDLOCAL

请注意,这假定程序产生的输出不超过一行。
参数"delims="告诉循环命令忽略任何分隔符,并将整个行视为单个值。 %%L是循环变量,循环体在本例中只包含一个命令,即跟随DO的命令。如果您想添加更多命令,请将其括在括号中。如果将它们放在同一行上,则可以使用&分隔命令,或者您可以将主体拆分成多行,如下所示:
FOR /F "delims=" %%L IN ('someprog.exe') DO (
  SET "PROGOUTPUT=%%L"
  do something else
)

如果您的程序可以被教会如何返回退出码,还有一种完全不同的方法。使用0表示成功返回,使用非零代码表示任何失败。在这种情况下,您的批处理脚本可能看起来像这样:

someprog.exe && (
  do something in case of "success" (one or more commands)
) || (
  do something in case of "failure" (one or more commands)
)

你可能仍然希望生成一个输出作为结果的可视指示(当单独从批处理脚本运行程序时可能有用)。要在批处理脚本中运行时隐藏输出,只需将其重定向到NUL

someprog.exe > NUL && (
...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接