我如何杀死所有 cmd.exe,除了当前从批处理中运行的那个?
Posted
技术标签:
【中文标题】我如何杀死所有 cmd.exe,除了当前从批处理中运行的那个?【英文标题】:how do I kill all cmd.exe except the one currently running from batch? 【发布时间】:2015-06-05 21:56:22 【问题描述】:在过去的几天里,我一直在编写一个脚本,我认为这很容易,但似乎并不容易,我明白为什么。我的问题是如何解决它。
我需要的批处理脚本说明:
我有一个在 cmd.exe 中运行的脚本,它可以执行很多操作,例如将大量文件从一个位置移动到另一个位置。让我们称之为
movefile.cmd
。该脚本有效,但有时会停止(很少 - 让我们不讨论为什么和那个脚本)。该脚本始终运行很重要,因此我的想法是创建一个退出 cmd.exe 的批处理,然后每隔一小时左右重新打开脚本。让我们将此脚本称为restartcmd.bat
听起来很简单,因为我可以这样做:
@echo off
:loop
start c:\script\movefile.cmd
Timeout /nobreak /t 3600
Taskkill cmd.exe
goto loop
但显然这不起作用,因为我的新脚本也在 cmd.exe 中运行,所以它也会杀死这个进程。
我的尝试:
所以我复制了 cmd.exe 并将其重命名为 dontkillthis.exe。我运行 dontkillthis.exe,然后从 dontkillthis.exe 打开 restardcmd.bat - 这非常有效!但我需要能够只是 dobbleclick 我的脚本而不是这样做。为什么?因为它应该尽可能简单,我希望我的 restartcmd.bat 位于我的启动文件夹中。
我一直在研究获取 cmd.exe 的确切进程 ID 并关闭它以便我的 dontkillthis.exe 保留的想法,但我似乎无法确定它。尝试了这里写的所有内容 how to kill all batch files except the one currently running ,但我无法让它工作。
我不确定我是否感到困惑,或者这样做是否真的有点困难。
我真的很感谢这里的一些帮助。
最好的问候
MO
【问题讨论】:
肮脏的方式:创建cmd
的副本为kill-me.exe
,使用start kill-me c:\script\movefile.cmd
& 然后taskkill kill-me.exe
(这是一个快速破解。应该有更好的方法。)
Get PID of current cmd.exe,然后taskkill
可以选择基于PID !=xxx
进行过滤检查taskkill /?
。
【参考方案1】:
首先您需要当前 CMD 实例的 PID。该主题有 been discussed here 。我会为你提供我的解决方案 - getCmdPID.bat
这是脚本(getCmdPID 应该在同一目录中):
@echo off
call getCmdPID
set "current_pid=%errorlevel%"
for /f "skip=3 tokens=2 delims= " %%a in ('tasklist /fi "imagename eq cmd.exe"') do (
if "%%a" neq "%current_pid%" (
TASKKILL /PID %%a /f >nul 2>nul
)
)
【讨论】:
Npocmaka,实际上(令我惊讶的是)就像一个魅力!谢啦。正是我想要的!发现。 :)【参考方案2】:通常使用以下命令我应该能够找到 PID。不幸的是,事实并非如此。
title exclude &tasklist /NH /v /fo csv /FI "WINDOWTITLE ne exclude*" /FI "IMAGENAME eq cmd.exe" /FI "STATUS eq running"
所以为了实现我的目标,我使用了以下命令:
FIND /I "exclude" 1>NUL
@echo off
TITLE exclude
(for /f "usebackq tokens=*" %%a in (`tasklist /NH /v /fo csv /FI "IMAGENAME eq cmd.exe" /FI "STATUS eq running"`) do (
(
echo %%a | FIND /I "exclude" 1>NUL
) || (
for /f "usebackq tokens=2 delims=," %%i in (`echo %%a`) do (
echo TASKKILL /PID %%~i /f
)
)
)
)>_output-taskill.txt
TYPE _output-taskill.txt
在一行中杀死所有进程的另一种方法是在命令taskkill
上使用过滤器,过滤器应如下所示:
TASKKILL /F /FI "PID ne XXXX" /FI "IMAGENAME eq cmd.exe" /IM cmd.exe
eq(等于)
ne(不等于)
gt(大于)
lt(小于)
@echo off
TITLE exclude
(for /f "usebackq tokens=2 delims=," %%a in (`tasklist /NH /v /fo csv /FI "IMAGENAME eq cmd.exe" /FI "STATUS eq running" ^| FIND /I "exclude"`) do (
echo TASKKILL /F /FI "PID ne %%~a" /FI "IMAGENAME eq cmd.exe" /IM cmd.exe
)
)>_output-taskill.txt
TYPE _output-taskill.txt
【讨论】:
【参考方案3】:我找到了一个解决方案,它利用文本文件来跟踪 bat 文件具有的所有先前的 PID。它会尝试以静默方式杀死它们,然后将当前 PID 添加到列表中。
如果您不希望它杀死旧的、已经存在的进程,只需将带有“taskkill”的行替换为您想要对其执行的任何操作即可。
(可能需要您以管理员身份运行才能获得杀死重复进程的权限。如果您不想每次都以管理员身份运行,请参阅下面的权限提升代码以获取可选实现.)
@echo off
set WorkingDir=%cd%
if exist MostRecentPID.txt ( del "PIDinfo.txt" /f /q ) > nul
cd ..\..\..\..\..\..\..
title mycmd
tasklist /v /fo csv | findstr /i "mycmd" > %WorkingDir%\PIDinfo.txt
set /p PIDinfo=<%WorkingDir%\PIDinfo.txt
REM below, the 11 means get substring starting a position 11 with length of 5 characters. The tasklist command gives a long and verbose value so this will get just the PID part of the string.
set PID5chars=%PIDinfo:~11,5%
set PID4chars=%PIDinfo:~11,4%
if exist PreviousPIDs.txt (
for /F "tokens=*" %%A in (PreviousPIDs.txt) do taskkill.exe /F /T /PID %%A > nul 2>&1
goto CheckIfFourCharPID
)
:CheckIfFourCharPID
if %PID4chars% gtr 8100 (
for /F "tokens=*" %%A in (PreviousPIDs.txt) do taskkill.exe /F /T /PID %%A > nul 2>&1
echo %PID4chars% >> "PreviousPIDs.txt"
) else (
echo %PID5chars% >> "PreviousPIDs.txt"
)
解释:(警告:技术性很强)
-此解决方案获取 tasklist 命令的子字符串以仅获取 PID。 cmd.exe 的 PID 不会大于 18100,因此请检查 PID4chars 是否大于 8100,以便我们知道它是 4 位数字还是 5 位数字
案例1:5位PID如17504有一个PID5chars val 17504和一个PID4chars val 1750,所以我们将PID5chars添加到PIDs的文本文件中以杀死
案例2:像8205这样的4位PID,PID5chars val为8205",PID4chars val为8205,所以我们将PID4chars添加到PIDs的文本文件中进行kill
案例3:4位PID如4352的PID5chars val为4352",PID4chars val为4352,所以我们将PID4chars添加到PIDs的文本文件中进行kill
可选的许可海拔代码
(把它放在你的 bat 文件的顶部,它会自动以管理员身份运行它。)
@echo off
setlocal DisableDelayedExpansion
set "batchPath=%~0"
for %%k in (%0) do set batchName=%%~nk
cd ..\..\..\..\..\..\..\..
if exist %cd%\Temp (
set temp=%cd%\Temp
goto vbsGetPrivileges
)
if exist %cd%\Windows\Temp (
set temp=%cd%\Windows\Temp
goto vbsGetPrivileges
)
set temp=%cd%
:vbsGetPrivileges
set "vbsGetPrivileges=%temp%\OEgetPriv_%batchName%.vbs"
setlocal EnableDelayedExpansion
:CheckIfRunningAsAdmin
net session >nul 2>&1
if %ERRORLEVEL% == 0 (
goto gotPrivileges
) else ( goto ElevatePermissions )
:ElevatePermissions
if '%1'=='ELEV' (echo ELEV & shift /1 & goto gotPrivileges)
ECHO Set UAC = CreateObject^("Shell.Application"^) > "%vbsGetPrivileges%"
ECHO args = "ELEV " >> "%vbsGetPrivileges%"
ECHO For Each strArg in WScript.Arguments >> "%vbsGetPrivileges%"
ECHO args = args ^& strArg ^& " " >> "%vbsGetPrivileges%"
ECHO Next >> "%vbsGetPrivileges%"
ECHO UAC.ShellExecute "!batchPath!", args, "", "runas", 1 >> "%vbsGetPrivileges%"
"%SystemRoot%\System32\WScript.exe" "%vbsGetPrivileges%" %*
exit /B
:gotPrivileges
setlocal & pushd .
cd /d %~dp0
if '%1'=='ELEV' (del "%vbsGetPrivileges%" 1>nul 2>nul & shift /1)
net session >nul 2>&1
if %ERRORLEVEL% == 0 (
goto Continue
) else (
REM unable to elevate permissions so tell user to run file as admin manually
echo Please re-run this file as administrator. Press any key to exit...
pause > nul
goto Exit
)
:Continue
<insert rest of code here>
【讨论】:
【参考方案4】:获得movefile.cmd
的PID 会好得多。如果您可以编辑它,请添加 title MyMoveFileProcess
并获取它的 PID
for /f "tokens=2" %%i in ('tasklist /v ^|find "MyMoveFileProcess"') do set PID=%%i
然后你可以用taskkill /pid %pid%
杀死它
除了更改您的movefile.cmd
,您也可以只用标题开头:
start "MyMoveFileProcess" c:\script\movefile.cmd
【讨论】:
第一个代码完全失败 - 它列出了所有 cmd.exe 进程。设计存在根本缺陷。 @dbenham 是的,确实如此(就像我写的那样)。也许我最好删除那部分。 @dbenham 另一方面:问题标题说“如何杀死所有 cmd.exe 除了...”我同意这是一个不好的方法。 编辑了标题,因为我意识到这确实是一个糟糕的方法。作为记录,npocmaka 的回答完成了这项工作。感谢您的投入!【参考方案5】:另外,仅供参考。
我发现可以根据标题停止和启动其他 cmd 文件,这将是我问题解决方案的替代方案。因此,假设movefile.cmd
在其脚本中有这一行:Title "movefile"
(" " 很重要)。
然后我可以在restartcmd.bat
中写taskkill /F /FI "WindowTitle eq Administrator: \"movefile"" /T
:)
【讨论】:
【参考方案6】:几行代码将帮助您实现这一目标:
TITLE exclude
taskkill /IM cmd.exe /FI "WINDOWTITLE ne exclude*"
【讨论】:
以上是关于我如何杀死所有 cmd.exe,除了当前从批处理中运行的那个?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 cmd.exe 中将变量值从 conftest.py 命令到 pytest