使用 GUI 应用程序的启动命令时批量输出重定向

发布于 2024-07-30 12:21:21 字数 768 浏览 7 评论 0原文

场景如下:

我们有一个 Python 脚本,它启动一个 Windows 批处理文件并将其输出重定向到一个文件。 然后它读取该文件,然后尝试删除它:

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

在batch.bat中,我们启动一个Windows GUI程序,如下所示:

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 命令来启动程序而不继承批处理输出重定向?

This is the scenario:

We have a Python script that starts a Windows batch file and redirects its output to a file. Afterwards it reads the file and then tries to delete it:

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

In the batch.bat we start a Windows GUI programm like this:

start c:\the_programm.exe

Thats all in the batch fíle.

Now the os.remove() fails with "Permission denied" because the temp.txt is still locked by the system. It seems this is caused by the still runing the_programm.exe (whos output also seems to be redirected to the temp.txt).

Any idea how to start the_programm.exe without having the temp.txt locked while it is still running? The Python part is hardly changeable as this is a tool (BusyB).

In fact I do not need the output of the_programm.exe, so the essence of the question is: How do I decouple the_programm.exe from locking temp.txt for its output?
Or: How do I use START or another Windows command to start a program without inheriting the batch output redirection?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

人心善变 2024-08-06 12:21:21

这有点hacky,但你可以尝试一下。 它使用 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 /? 以了解它们各自的作用。 因为您评论说“不允许用户交互”,所以我省略了 AT/interactive 参数。

注意:

  • 无论控制面板中的区域设置如何,%TIME% 似乎始终是 24 小时时间,但我没有任何证据证明这一点。
  • 如果您的系统已加载且batch.bat 运行时间超过10 秒,AT 命令将安排在1 天后运行。 您可以使用 AT {job} /delete 手动恢复此设置,并将 x=70 增加到更可接受的值。

不幸的是,即使给 /i 忽略当前环境,START 命令似乎也会传递父 cmd.exe 过程。 这些文件描述符似乎被移交给子进程,即使子进程被重定向到 NUL,并且即使中间 shell 进程终止也保持打开状态。 如果您有一个批处理文件,您可以在 Process Explorer 中看到这一点START是另一个批处理文件,START是另一个批处理文件(等等),START是 GUI Windows 应用程序。 一旦中间批处理文件终止,GUI 应用程序将拥有文件句柄,即使它(和中间批处理文件)全部重定向到 NUL

This is a bit hacky, but you could try it. It uses the AT command to run the_programm.exe up to a minute in the future (which it computes using the %TIME% environment variable and SET arithmetic).

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

You can look at the AT /? and SET /? to see what each of these is doing. I left off the /interactive parameter of AT since you commented that "no user interaction is allowed".

Caveats:

  • It appears that %TIME% is always 24-hour time, regardless of locale settings in the control panel, but I don't have any proof of this.
  • If your system is loaded down and batch.bat takes more than 10 seconds to run, the AT command will be scheduled to run 1 day later. You can recover this manually, using AT {job} /delete, and increase the x=70 to something more acceptable.

The START command, unfortunately, even when given /i to ignore the current environment, seems to pass along the open file descriptors of the parent cmd.exe process. These file descriptors appear to be handed off to subprocesses, even if the subprocesses are redirected to NUL, and are kept open even if intermediate shell processes terminate. You can see this in Process Explorer if you have a batch file which STARTs another batch file which STARTs another batch file (etc.) which STARTs a GUI Windows app. Once the intermediate batch files have terminated, the GUI app will own the file handles, even if it (and the intermediate batch files) were all redirected to NUL.

幸福丶如此 2024-08-06 12:21:21

我认为 Windows 不会让你删除打开的文件。 听起来你想扔掉程序的输出; 会重定向到“nul”而不是做你需要的事情吗?

I don't think Windows will let you delete an open file. Sounds like you're wanting to throw away the program's output; would redirecting to 'nul' instead do what you need?

愿与i 2024-08-06 12:21:21

据我了解,这就是问题所在,也是他想要做的:

  1. 对 python 代码进行更改。
  2. 编写 Python 代码时假设此函数返回时不再使用“temp.txt”:

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

  3. 事实上并非如此,因为“batch.bat”使用以下命令生成交互式 GUI 程序: 。

如何更改“batch.bat”文件以包含:

start c:\the_programm.exe
pause

这将使“batch.bat”文件保持运行,直到您按下该窗口上的某个键。 一旦按下某个键,“os.system”python 命令将返回,然后 python 将调用“os.remove”。

As I understand it, this is the issue, and what he wants to do:

  1. Make no changes to the python code.
  2. The Python code is written assuming that "temp.txt" is no longer being used when this function returns:

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

  3. This is in fact not the case because "batch.bat" spawns an interactive GUI program using the "start" command.

How about changing your "batch.bat" file to contain:

start c:\the_programm.exe
pause

This will keep the "batch.bat" file running until you hit a key on that window. Once you hit a key, the "os.system" python command will return, and then python will call "os.remove".

醉态萌生 2024-08-06 12:21:21

阅读完文件后是否要关闭该文件? 以下内容在我这边有效:

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() 行,则会失败。

Are you closing the file after you're done reading it? The following works at my end:

import os

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

but fails if I remove the f.close() line.

兮颜 2024-08-06 12:21:21

如果您只是立即删除它,为什么还要捕获到文件呢?

这个怎么样:

os.system(C:\batch.bat >nul 2>&1)

编辑:哎呀,我错过了你关于阅读文件的评论,我只注意到了代码。

Why capture to a file if you're just deleting it immediately?

How about this:

os.system(C:\batch.bat >nul 2>&1)

EDIT: Oops, I missed your comment about reading the file, I only noticed the code.

饮惑 2024-08-06 12:21:21

最后我找到了一个合适的解决方案:

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

from subprocess import Popen

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

close_fds 参数将文件句柄与 .exe 进程解耦! 就是这样!

Finally I could find a proper solution:

I am not using a batch file anymore for starting the_programm.exe, but a Python script:

from subprocess import Popen

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

The close_fds parameter decouples the file handles from the .exe process! That's it!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文