如何等待进程终止以在批处理文件中执行另一个进程

Posted

技术标签:

【中文标题】如何等待进程终止以在批处理文件中执行另一个进程【英文标题】:How to wait for a process to terminate to execute another process in batch file 【发布时间】:2011-12-31 22:30:52 【问题描述】:

如何在批处理文件中执行另一个进程之前等待一个进程终止?假设我有一个进程notepad.exe,在执行wordpad.exe 之前我需要杀死它。然后,当wordpad.exe 终止时,我需要再次启动notepad.exe。我该怎么做?

【问题讨论】:

@sarnold 问题是 Win32 是 ... Win32。一些程序(如 write)会从父进程分离,而其他程序(如记事本)则不会。 @pst:我以为你必须专门使用start 才能获得分离...哦哇。这可怕 @sarnold 哎呀:记事本不分离,写。它与 write 程序有关,不一定是 Win32,尽管许多 UI 程序(不打算从 shell 调用)似乎都是这样工作的。我相当肯定CreateProcess 是相关的。 @Apoc 我将包含一个时间线解释,其中包含一个示例脚本,其中包含进程之间的回声以显示发生的分离。如果没有分离,进程将按顺序运行。 【参考方案1】:

使用start /w programname等待程序名结束

START /W notepad
ECHO Back from notepad  
START /W wordpad
ECHO Back from wordpad
START /W notepad

【讨论】:

这比完整的批处理要简单得多,而且效果很好。请注意,如果您的程序名称使用引号,则需要添加 title 参数。请参阅help start 了解更多信息。 实际上,你为什么需要start /w?我自己认为需要使用start /w 来阻止批处理文件,直到GUI(与控制台相反)应用程序完成。但是现在测试它,我发现批处理文件实际上甚至在等待 GUI 应用程序。我已经发布了相应的问题:Why GUI application blocks a batch file? 这个解决方案只有在批处理文件也在启动进程并且不检查现有进程时才有效。 如果您想同时启动两个或多个进程并等待所有进程完成,这将不起作用。这就是为什么米奇·布兹·斯金格的回答更受国际海事组织欢迎。 如果启动的程序从它的调用进程中分离出来,它就不起作用(见问题的 cmets)。【参考方案2】:

试试这样的...

@ECHO OFF

PSKILL NOTEPAD

START "" "C:\Program Files\Windows NT\Accessories\wordpad.exe"

:LOOP
PSLIST wordpad >nul 2>&1
IF ERRORLEVEL 1 (
  GOTO CONTINUE
) ELSE (
  ECHO Wordpad is still running
  TIMEOUT /T 5
  GOTO LOOP
)

:CONTINUE
NOTEPAD

我使用了PSLISTPSEXEC,但你也可以使用TASKKILLTASKLIST>nul 2>&1 只是用来隐藏PSLIST 的所有输出。 SLEEP 5 行不是必需的,只是用来限制检查写字板是否仍在运行的频率。

【讨论】:

看起来TASKLIST 没有设置ERRORLEVE。您可以通过FINDTASKLIST 的输出作为解决方法。像这样,TASKLIST | FIND /I "wordpad" 这很弱。像大自然一样使用start /wait @DavidHeffernan START /WAIT 如果您希望一切都停止直到过程完成,这很好。我的简单示例并没有真正展示它,但是您可以使用这种技术来启动一个流程,然后做一些其他事情,但在第一个流程结束之前不要在某个点之后继续。 如果批处理文件没有启动该过程,这也可以工作,这有时很方便。 我有一个 Windows 服务部署脚本,它停止服务,然后部署文件,但在 net stop 调用完成后,.exe 并不总是完全退出,并且文件被锁定。这种方法正是我需要确保在复制文件之前服务实际上已经停止。谢谢。【参考方案3】:

这是 aphoria's Answer 的更新版本。

我将 PSLIST 和 PSEXEC 替换为 TASKKILL 和 TASKLIST`。由于它们似乎工作得更好,我无法让 PSLIST 在 Windows 7 中运行。

还将睡眠替换为 TIMEOUT。

这就是让脚本运行良好所需的一切,所有的补充都是由发布 cmets 的好人提供的。

此外,如果在 .exe 启动之前存在延迟,则可能值得在 :loop 之前插入 Timeout。

@ECHO OFF

TASKKILL NOTEPAD

START "" "C:\Program Files\Windows NT\Accessories\wordpad.exe"

:LOOP
tasklist | find /i "WORDPAD" >nul 2>&1
IF ERRORLEVEL 1 (
  GOTO CONTINUE
) ELSE (
  ECHO Wordpad is still running
  Timeout /T 5 /Nobreak
  GOTO LOOP
)

:CONTINUE
NOTEPAD

【讨论】:

这不是我答案的“更新”版本。这是我的答案的副本,只是用TASKKILLTASKLIST 替换了PSLISTPSEXEC,我在我的答案中将它们列为替代品。 谢谢。如果该进程碰巧产生了额外的进程,因为 /wait 解决方案不起作用,这是最好的答案。只需将类似的代码用于附加流程即可解决此类情况。 @aphoria:我认为他发布这个是因为 psexec 和 pslist 不是(某些?)Windows 的一部分,它们是 sysinternals 程序。此外,如果它们是第一次运行,它们会弹出一个对话框以同意许可条款。使用起来并不理想,除非您控制目标计算机并且可以预先安装这些工具。它绝对是“更新的”:您需要知道如何使用find(或findstr)来完成这项工作(pslist 在这方面更容易)。尽管使用此版本编辑您的答案就足够了,imo。 @Abel 在发布此答案之前,我在我的答案中列出了 TASKKILLTASKLIST 作为替代品。此外,这对我的回答并不重要,但 SysInternals 实用程序支持 /accepteula 开关。 @aphoria,没错,你的回答很好。这个可以帮助那些不能依赖那些工具的人,这是完全不同的,它给了你信任并帮助了我和其他人。不知道那个开关,谢谢。【参考方案4】:

我喜欢“START /W”的答案,但就我的情况而言,我发现了一些更基本的东西。我的进程是控制台应用程序。在我的无知中,我认为我需要一些特殊的 BAT 语法来确保第一个在第二个开始之前完成。然而,BAT 似乎区分了控制台应用程序和 Windows 应用程序,并且执行它们的方式略有不同。 OP 显示窗口应用程序将作为来自 BAT 的异步调用启动。但是对于控制台应用程序,它们是在与 BAT 本身运行所在的同一命令窗口中同步调用的。

对我来说,实际上最好不要使用“START /W”,因为一切都可以在一个命令窗口中运行。 “START /W”的烦人之处在于它会产生一个新的命令窗口来执行你的控制台应用程序。

【讨论】:

使用开关 /B 它将在同一个窗口中执行。 啊,我明白了。但是直接调用“My.exe”与“START /B My.exe”有什么区别吗?好像是一样的。【参考方案5】:

这很有效,而且更简单。如果去掉 ECHO-s,它会更小:

REM
REM DEMO - how to launch several processes in parallel, and wait until all of them finish.
REM

@ECHO OFF
start "!The Title!" Echo Close me manually!
start "!The Title!" Echo Close me manually!
:waittofinish
echo At least one process is still running...
timeout /T 2 /nobreak >nul
tasklist.exe /fi "WINDOWTITLE eq !The Title!" | find ":" >nul
if errorlevel 1 goto waittofinish
echo Finished!
PAUSE

【讨论】:

fwiw:在我的 windows 10 笔记本电脑上,当我将 aboe 保存到一个 .bat 文件并从命令提示符调用它时,它起作用了(当我剪切并粘贴到命令窗口时它失败了)【参考方案6】:

'start /w' 并非在所有情况下都有效。原始解决方案效果很好。但是,在某些机器上,最好在启动可执行文件后立即延迟。否则,任务名称可能还没有出现在任务列表中,并且循环将无法按预期工作(有人指出)。可以组合 2 个延迟并将其放在循环的顶部。也可以去掉'else'只是为了缩短。顺序运行的 2 个程序示例:

c:\prog1.exe 
:loop1
timeout /t 1 /nobreak >nul 2>&1
tasklist | find /i "prog1.exe" >nul 2>&1
if errorlevel 1 goto cont1
goto loop1
:cont1

c:\prog2.exe
:loop2
timeout /t 1 /nobreak >nul 2>&1
tasklist | find /i "prog2.exe" >nul 2>&1
if errorlevel 1 goto cont2
goto loop2
:cont2

约翰·雷弗林

【讨论】:

这对我很有用。我用它来运行 pyinstaller。在将一些配置文件复制到同一输出文件夹之前,我需要确保 pyinstaller 进程已完成生成其 /dist/... 文件夹输出。如果我不等待 pyinstaller 进程结束,输出文件夹会因为某种原因消失。【参考方案7】:

这是我改编的 johnrefling 的。 这项工作也适用于 WindowsXP; 就我而言,我最后启动了相同的应用程序, 因为我想用不同的参数重新打开它。 我的应用程序是一个 WindowForm .NET

@echo off
taskkill -im:MyApp.exe
:loop1
tasklist | find /i "MyApp.exe" >nul 2>&1
if errorlevel 1 goto cont1
echo "Waiting termination of process..."
:: timeout /t 1 /nobreak >nul 2>&1      ::this don't work in windows XP
:: from: https://***.com/questions/1672338/how-to-sleep-for-five-seconds-in-a-batch-file-cmd/33286113#33286113
typeperf "\System\Processor Queue Length" -si 1 -sc 1 >nul s
goto loop1
:cont1
echo "Process terminated, start new application"
START "<SYMBOLIC-TEXT-NAME>" "<full-path-of-MyApp2.exe>" "MyApp2-param1" "MyApp2-param2"
pause

【讨论】:

【参考方案8】:
call process1
call process2

在这种情况下,process2 在 process1 完成之前不会开始。

【讨论】:

您应该更仔细地阅读问题(和 cmets)。在第一条评论中,它解释了为什么 CALL 不是通用解决方案

以上是关于如何等待进程终止以在批处理文件中执行另一个进程的主要内容,如果未能解决你的问题,请参考以下文章

Windows批处理 调用程序后 不等待子进程 父进程继续执行命令

在批处理文件中休眠

如何获取批处理文件以执行下一个命令而无需等待

优雅地终止从带有“start /b”的 Windows 批处理文件启动的 java (jetty/solr) 进程

从批处理文件的进程中读取 InputStream 会跳到下一行

QProcess 终止进程树