使用start命令启动GUI应用时的批处理输出重定向

0 投票
6 回答
2110 浏览
提问于 2025-04-15 13:34

这是一个场景:

我们有一个Python脚本,它会启动一个Windows批处理文件,并把输出内容重定向到一个文件里。之后,它会读取这个文件,然后尝试删除它:

os.system(C:\batch.bat >C:\temp.txt 2>&1)
os.remove(C:\temp.txt)

在batch.bat文件中,我们这样启动一个Windows图形界面程序:

start c:\the_programm.exe

这就是批处理文件里的所有内容。

现在,os.remove()在删除时失败,显示“权限被拒绝”,因为temp.txt文件仍然被系统锁定。看起来这是因为the_programm.exe还在运行(它的输出似乎也被重定向到了temp.txt)。

有没有办法在the_programm.exe运行时,不让temp.txt被锁定?Python部分几乎不能更改,因为这是一个工具(BusyB)。

实际上,我并不需要the_programm.exe的输出,所以问题的核心是:如何让the_programm.exe运行时不锁定temp.txt?或者:如何使用START或其他Windows命令启动一个程序,而不继承批处理的输出重定向?

6 个回答

0

你在读完文件后有关闭它吗?我这边下面的代码是可以正常工作的:

import os

os.system('runbat.bat > runbat.log 2>&1')
f = open('runbat.log')
print f.read()
f.close()
os.remove('runbat.log')

但是如果我把 f.close() 这一行去掉,就会出问题。

2

这个方法有点小技巧,但你可以试试。它使用了 AT 命令来在未来的一分钟内运行 the_programm.exe(这个时间是通过 %TIME% 环境变量和 SET 算术计算出来的)。

这是一个批处理文件:batch.bat

@echo off
setlocal
:: store the current time so it does not change while parsing
set t=%time%
:: parse hour, minute, second
set h=%t:~0,2%
set m=%t:~3,2%
set s=%t:~6,2%
:: reduce strings to simple integers
if "%h:~0,1%"==" " set h=%h:~1%
if "%m:~0,1%"=="0" set m=%m:~1%
if "%s:~0,1%"=="0" set s=%s:~1%
:: choose number of seconds in the future; granularity for AT is one
:: minute, plus we need a few extra seconds for this script to run
set x=70
:: calculate hour and minute to run the program
set /a x=s + x
set /a s="x %% 60"
set /a x=m + x / 60
set /a m="x %% 60"
set /a h=h + x / 60
set /a h="h %% 24"
:: schedule the program to run
at %h%:%m% c:\the_programm.exe

你可以查看 AT /?SET /? 来了解这些命令的具体作用。我没有加上 /interactive 参数,因为你提到“不能有用户交互”。

注意事项:

  • 看起来 %TIME% 总是以24小时制显示时间,不管控制面板的区域设置是什么,但我没有证据证明这一点。
  • 如果你的系统负担很重,导致 batch.bat 运行超过10秒,AT 命令会被安排在1天后运行。你可以手动恢复这个任务,使用 AT {job} /delete,并把 x=70 增加到一个更合适的值。

不幸的是,START 命令即使加上了 /i 来忽略当前环境,似乎还是会传递父进程 cmd.exe 的打开文件描述符。这些文件描述符会被传递给子进程,即使子进程被重定向到 NUL,并且即使中间的 shell 进程结束,这些文件描述符仍然会保持打开状态。如果你有一个批处理文件,它 START 另一个批处理文件,然后又 START 另一个(以此类推),最后 START 一个图形界面的 Windows 应用程序,你可以在 Process Explorer 中看到这个现象。一旦中间的批处理文件结束,图形应用程序将拥有这些文件句柄,即使它(和中间的批处理文件)都被重定向到 NUL

0

最后我找到了一个合适的解决办法:

我不再使用批处理文件来启动 the_programm.exe,而是用一个 Python 脚本:

from subprocess import Popen

     if __name__ == '__main__':
          Popen('C:/the_programm.exe', close_fds=True)

close_fds 参数可以把文件句柄和 .exe 进程分开!就这么简单!

撰写回答