BATCH - 将命令输出存储到变量中

5
我想编写一个批处理脚本,只有当cmd.exe的运行次数少于2次时才执行某个操作。我找到了一种解决方案,它将出现次数存储在临时文件中,但我觉得这样做非常不优雅。所以我的问题是如何在不使用临时文件的情况下完成与下面相同的操作,而是通过将@TASKLIST | FIND /I /C "%_process%"给出的出现次数存储在变量中来实现。我看到可能有一个使用for循环的解决方案,但我无法让它工作,无论如何,我真的更喜欢基于SET的解决方案(如果可能的话)。
@SET _process=cmd.exe
@SET _temp_file=tempfiletodelete.txt
@TASKLIST | FIND /I /C "%_process%" > %_temp_file%
@SET /p _count=<%_temp_file%
@DEL /f %_temp_file%
@IF %_count% LSS 2 (
    @ECHO action 1
) ELSE (
    @ECHO action 2
)

编辑: 这个问题类似于保存FIND命令的输出到变量,但我无法将解决方案应用于我的问题,并且我想知道是否有一种没有for循环的解决方案。


for /F %%a in ('@TASKLIST ^| FIND /I /C "%_process%"') do @SET "_count=%%a" - Aacini
要捕获命令的输出,您需要使用for / F(需要像^|这样转义管道); 除了使用临时文件之外,没有基于set[/P]的方法... - aschipfl
1
可能是Save output from FIND command to variable的重复问题。 - aschipfl
2个回答

6

使用带有选项/F的命令FOR和指定为set(括号内的字符串)的命令或命令行,另外加上',可用于处理一个命令或多个命令行中的输出。

您可以使用此批处理文件:

@ECHO OFF
SET "_process=cmd.exe"
FOR /F %%I IN ('%SystemRoot%\System32\tasklist.exe /FI "IMAGENAME eq %_process%" ^| %SystemRoot%\System32\find.exe /I /C "%_process%"') DO SET "_count=%%I"
IF /I "%_process%" == "cmd.exe" SET /A _count-=1
IF %_count% LSS 2 (
    ECHO action 1
) ELSE (
    ECHO action 2
)

命令FOR在后台运行,没有可见的控制台窗口,使用以下命令行 cmd.exe /C

C:\Windows\System32\tasklist.exe /FI "IMAGENAME eq cmd.exe" | C:\Windows\System32\find.exe /I /C "cmd.exe"
TASKLIST 命令选项 /FI "IMAGENAME eq cmd.exe" 已经将 TASKLIST 命令的输出过滤为进程名(即可执行文件名)为 cmd.exe 的进程。这使得进一步处理更快,并避免了运行文件名为 totalcmd.exe 的进程被计算为 cmd.exe 的情况。 TASKLIST 命令的输出被重定向到 FIND 命令的 STDIN 句柄,FIND 命令处理这些行并计算包含任何位置的 cmd.exe 的行数。最终,FIND 命令将包含搜索字符串的行数输出到后台运行的命令进程的 STDOUT 句柄。
在 Windows 命令解释器在执行嵌套命令行之前处理此命令行时,必须使用插入符号 ^| 重定向运算符进行转义,以便在执行嵌套命令行时将其解释为字面字符。 FOR 命令选项 /F 捕获执行的命令进程的 STDOUT 句柄中写入的所有内容并处理每一行。在这种情况下,FOR 命令只捕获包含一个数字的一行。因此,不需要使用其他选项将 FOR 分配给循环变量 I 的数字分配给环境变量 _count
此批处理文件的目标是计算已运行的 cmd.exe 进程的数量。由于使用一个额外的 cmd.exe 进程在后台执行 TASKLISTFIND 命令行,因此需要通过使用 SET /A _count-=1 计算表达式来减去一个计数,以获得正确的结果。这个减一只是为了正确计算 cmd.exe 进程的数量。对于任何其他进程,这是不必要的。
为了理解所使用的命令及其工作原理,请打开命令提示符窗口,在其中执行以下命令,并仔细阅读每个命令显示的所有帮助页面。
  • echo /?
  • find /?
  • for /?
  • if /?
  • set /?
  • tasklist /?

@Mofi 谢谢您的回答,它完美地解决了我的问题,我已经接受了它。有一件事我不明白,为什么您使用 %SystemRoot%\System32\tasklist.exe%SystemRoot%\System32\find.exe 而不是 TASKLISTFIND - nico
1
@nico 请查看为什么“...”不被识别为内部或外部命令、可操作的程序或批处理文件的原因是什么? 有很多程序员和应用程序安装程序会破坏环境变量PATH,因此编写批处理文件时尽可能地通过始终使用完全限定的文件名(=完整路径+文件名+文件扩展名)来引用可执行文件,以使其独立于环境变量PATHPATHEXT,这是一种编码安全性问题。 - Mofi

5
您可以像这样做,使用TaskListFor循环中:
@Set "_process=cmd.exe"
@For /F %%A In ('TaskList^|Find /C /I "%_process%"'
) Do @If %%A Lss 2 (Echo Action 1) Else Echo Action 2
@Pause

您也可以使用 WMIC 来执行类似的操作:

@Set "_process=cmd.exe"
@For /F %%A In ('WMIC Process Get Name^|Find /C "%_process%"'
) Do @If %%A Lss 2 (Echo Action 1) Else Echo Action 2
@Pause

可以进一步优化这些内容,以确保包含该字符串的流程不会被计算在内,而不是与其匹配的流程:

@Set "_process=cmd.exe"
@For /F %%A In ('TaskList /FI "ImageName Eq "%_Process%""^|Find /C "."'
) Do @If %%A Lss 2 (Echo Action 1) Else Echo Action 2
@Pause

 

@Set "_process=cmd.exe"
@For /F %%A In (
    'WMIC Process Where "Name='%_process%'" Get Name^|Find /C "."'
) Do @If %%A Lss 2 (Echo Action 1) Else Echo Action 2
@Pause
注意:
如果你正在检查 cmd.exe 进程,请注意 For 循环括号内的命令会在一个新的 cmd.exe 实例中运行,因此会增加计数器 1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接