调用Windows脚本时Python返回错误退出代码

6 投票
3 回答
4054 浏览
提问于 2025-04-16 21:11

我在Windows上使用subprocess.call时,似乎得不到正确的退出代码。

import subprocess
exit_code = subprocess.call(['ant.bat', 'fail'])
print exit_code # prints 0

在Windows上做同样的事情,返回的结果似乎不是0。

> echo %errorlevel%
0
> ant fail
> echo %errorlevel%
1

这两个调用的结果不应该是一样的吗?我是不是做错了什么?

最糟糕的情况下,我该如何在我的Python脚本中检查%errorlevel%的值?

更新:

我尝试了类似这样的方式来获取errorlevel的值:

environment = os.environment.copy()
cmd = subprocess.Popen(['ant.bat', 'fail'], env = environment)
for key, value in environment.items():
    print '%s = %s' % (key, value)

但是我在那个字典中没有看到errorlevel(os.getenv['errorlevel']也不行)。

3 个回答

-1

os.system('ant.bat fail') 这段代码正好实现了你想要的功能。它会返回错误级别的信息。

0

我通过使用批处理中的 call 命令,成功实现了正确的效果,代码如下:

cmd = [os.environ['COMSPEC'], '/c', 'call', bat_file]
try:
    subprocess.check_call(cmd)
except subprocess.CalledProcessError:
    # Error handling code

(我使用了 subprocess.check_call,不过 subprocess.call 应该也能达到同样的效果。)

在你的批处理脚本中的每个命令后面加上 if errorlevel 1 exit 1 是个好主意,这样可以传递错误信息(大致相当于 bash 中的 set -e)。

8

进程的退出代码和错误级别环境变量其实是两回事:

ant.bat:

if "%1"=="batch_fail" exit /B 1
if "%1"=="proc_fail" exit 1


>>> import subprocess
>>> subprocess.call(['ant.bat', 'batch_fail'])
0
>>> subprocess.call(['ant.bat', 'proc_fail'])
1

当batch_fail运行时,它会把错误级别设置为1,但在命令行窗口关闭后,这个错误级别就不再可用了。而proc_fail则会把进程的退出代码设置为1。想到的唯一解决办法就是写一个包装的批处理文件,这个文件会调用ant.bat,并根据错误级别来设置进程的退出代码:

ant_wrapper.bat:

@echo off
call ant.bat %1
if errorlevel 1 exit 1

>>> subprocess.call(['ant_wrapper.bat'])
0
>>> subprocess.call(['ant_wrapper.bat', 'batch_fail'])
1
>>> subprocess.call(['ant_wrapper.bat', 'proc_fail'])
1

补充:

你的更新让我想到另一种方法,可以使用Popen。你可以通过cmd的/K选项来运行批处理文件,这样可以在不退出的情况下执行命令。然后只需通过标准输入发送exit %errorlevel%,并进行通信:

#test errorlevel==1
>>> p = subprocess.Popen(['cmd', '/K', 'ant.bat', 'batch_fail'], 
      stdin=subprocess.PIPE, stdout=subprocess.PIPE)
>>> stdoutdata, stderrdata = p.communicate(b'exit %errorlevel%\r\n')
>>> p.returncode
1

#test errorlevel==0
>>> p = subprocess.Popen(['cmd', '/K', 'ant.bat'], 
      stdin=subprocess.PIPE, stdout=subprocess.PIPE)
>>> stdoutdata, stderrdata = p.communicate(b'exit %errorlevel%\r\n')
>>> p.returncode
0

撰写回答