除了当前从批处理运行的cmd.exe之外,如何杀死所有cmd.exe?

时间:2015-04-01 09:50:54

标签: windows batch-file cmd taskkill

过去几天我一直在编写一个脚本,我觉得这很容易,但似乎没有,我明白为什么。我的问题是如何解决它。

我需要解释的批处理脚本:

我有一个在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 - 这非常有效!但是我需要能够完成我的脚本而不是这样做。为什么?因为它应该尽可能简单,我希望我的restartcmd.bat在我的启动文件夹中。

我一直在考虑获取cmd.exe的确切进程ID并关闭它以使我的dontkillthis.exe保留的想法,但我似乎无法确定它。试过这里写的所有内容how to kill all batch files except the one currently running,但我无法让它发挥作用。

我不确定我是否感到困惑,或者实际上是否有点困难。

我非常感谢这里的一些帮助。

最好的问候

MO

6 个答案:

答案 0 :(得分:7)

首先,您需要当前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
    )
)

答案 1 :(得分:1)

通常使用以下命令我应该能够找到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

答案 2 :(得分:1)

我找到了一个解决方案,该解决方案利用文本文件来跟踪bat文件具有的所有先前的PID。它尝试以静默方式杀死它们,然后将当前的PID添加到列表中。

如果您不希望它杀死已经存在的旧进程,只需用您想对其执行的操作替换具有“ taskkill”的行。

(可能需要您以admin身份运行才能拥有杀死重复进程的权限。如果您不想每次都以admin身份运行,请参见下面的权限提升代码以了解可选实施。)

@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值是17504,而PID4chars值是1750,所以我们将PID5chars添加到PID的文本文件中以杀死

情况2:像8205这样的4位数PID的PID5chars值是8205“,而PID4chars值是8205,所以我们将PID4chars添加到PID的文本文件中以杀死

情况3:像4352这样的4位数PID的PID5chars值是4352“,而PID4chars值是4352,因此我们将PID4chars添加到PID的文本文件中以杀死


可选许可海拔代码

(将其放在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>

答案 3 :(得分:0)

获得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 

答案 4 :(得分:0)

另外,仅供其他人使用。 我发现可以根据标题停止和启动其他cmd文件,这可能是我问题解决方案的替代方案。所以我们可以说movefile.cmd在其脚本中有这一行:Title "movefile"(“”很重要)。 然后我可以在taskkill /F /FI "WindowTitle eq Administrator: \"movefile"" /T :)

中写restartcmd.bat

答案 5 :(得分:-2)

有几行可以帮助您实现这一目标:

TITLE exclude
taskkill /IM cmd.exe /FI "WINDOWTITLE ne exclude*"