【问题标题】:Detect if bat file is running via double click or from cmd window通过双击或从cmd窗口检测bat文件是否正在运行
【发布时间】:2011-08-17 02:54:23
【问题描述】:

我有一个 bat 文件,它可以做很多事情并关闭 cmd 窗口,当用户从资源管理器中双击 bat 文件时,这很好。但是,如果我从已经打开的 cmd 窗口运行 bat 文件,如 cmd>c:\myfile.bat ,那么我不希望 bat 文件关闭 cmd 窗口(END),因为我需要做其他事情。我需要能够执行类似操作的 bat dos 命令代码

if (initiated_from_explorer) then
else
endif

这可能吗?谢谢

【问题讨论】:

  • 从 Windows 资源管理器运行时不会自动关闭吗?
  • 正如迈克尔所说,你想要的是默认行为。听起来您在批处理文件的末尾有一个明确的退出命令。不要。
  • 反之亦然 - 如果从资源管理器启动,我想保留 pause 声明,但从 cmd.exe 启动时不保留。
  • 类似@ECHO %CMDCMDLINE% | FIND /I /C "/C" > NUL && PAUSE 应该这样做,除非命令窗口以奇怪的方式打开并包含 "/C"。
  • 顺便说一句,@ECHO %CMDCMDLINE% | FIND /I /C "/C" > NUL && TIMEOUT /T 9(或任何秒数)可能更有用,以避免在桌面上出现大量未关闭的窗口。

标签: windows cmd


【解决方案1】:

mousio's solution 很好;但是,由于%cmdcmdline% 的值中有双引号(%cmdcmdline% 周围有或没有双引号),我个人没有设法使其在“IF”语句中工作。

相比之下,使用%0 的解决方案可以正常工作。我使用了以下块语句,它就像一个魅力:

IF %0 == "%~0"  pause

以下解决方案将%~0 扩展为完全限定的路径,如果前一个解决方案不可用(参见Alex Essilfie's comment),也可能有效:

IF %0 EQU "%~dpnx0" PAUSE

但是,请注意,%~dpnx0 的解决方案会在以下情况下失败

  1. .bat 文件位于%USERPROFILE% 目录中的某处,并且
  2. 您的%USERNAME% 包含一个或多个大写字符

因为...等待它...%~dpnx0 中的d 将您的%USERPROFILE% 用户名强制为小写,而普通的%0 不会。因此,如果您的用户名包含大写字符,它们永远不会相等。 ¯\_(ツ)_/¯

[2021 年 6 月 18 日编辑 - 感谢 JasonXA]

您可以通过不区分大小写的比较来解决这个小写问题(魔术/I):

IF /I %0 EQU "%~dpnx0" PAUSE

这可能是最好的解决方案!

【讨论】:

  • 这是最直接的一个。就我而言,我发现我必须在第二个参数中附加几个字符,以便将其扩展为完全限定的路径并使其正确评估。所以,我的最终命令是IF %0 EQU "%~dpnx0" PAUSE
  • @AlexEssilfie 很高兴它可以提供帮助,并感谢您的反馈。解决方案已更新。
  • 这两项检查都可以通过命令行通过指定完整路径并在其周围加上引号来调用脚本来欺骗...
  • @Jean-FrançoisCorbett 另一个让-弗朗索瓦正在编辑我的答案。几率是多少?谢谢 J-F。
  • 如果/我会解决不同的案例比较。
【解决方案2】:

%cmdcmdline% 给出了用于启动当前 Cmd.exe 的确切命令行。

  • 从命令控制台启动时,此变量为 "%SystemRoot%\system32\cmd.exe"
  • 从资源管理器启动时,此变量为 cmd /c ""{full_path_to_the_bat_file}" ";
    这意味着您可能还会检查 bat 文件中的 %0 变量,因为在这种情况下它始终是 bat 文件的完整路径,并且始终用双引号括起来。

就个人而言,我会选择%cmdcmdline% 方法(不是%O),但请注意这两个启动命令都可以在注册表中被覆盖……

【讨论】:

  • 除了从 powershell 运行 ./batch.cmd 使用 cmd /c 之外的其他工作 :(
【解决方案3】:

一个综合答案,来源于此页面上的大部分信息:

:pauseIfDoubleClicked
setlocal enabledelayedexpansion
set testl=%cmdcmdline:"=%
set testr=!testl:%~nx0=!
if not "%testl%" == "%testr%" pause
  1. 变量“testl”获取 cmd 处理器调用的完整行(根据 mousio),去掉所有讨厌的双引号。
  2. 变量“testr”采用“testl”并进一步去除当前批处理文件的名称(如果存在)(如果批处理文件是通过双击调用的)。
  3. if 语句查看“testl”和“testr”是否不同。如果是,批处理被双击,所以暂停;如果不是,批处理是在命令行上输入的,继续。

当然,如果你想在检测到双击时做其他事情,你可以改变暂停。

谢谢大家。

【讨论】:

  • 作为一个额外的好处,这在以管理员身份使用鼠标启动时也可以工作。
【解决方案4】:

代码更少,更健壮:

在其他答案的基础上,我找到了最可靠的方法:

  1. 将引号替换为 x 以启用文本比较而不破坏 IF 语句。
  2. 准确地重新创建预期的 cmdcmdline(好吧,将 '"' 替换为 x)。
  3. 测试不区分大小写的相等性。

结果是:

set "dclickcmdx=%comspec% /c xx%~0x x"
set "actualcmdx=%cmdcmdline:"=x%"

set isdoubleclicked=0
if /I "%dclickcmdx%" EQU "%actualcmdx%" (
    set isdoubleclicked=1
)

这增加了对一般 cmd /c 调用的鲁棒性,因为 Explorer 在最后一个引号 /x 之前添加了一个尴尬的额外空间(对我们有利)。如果找不到cmdcmdline,它会正确呈现isdoubleclicked=0

附录:

与上面的方法类似,如果从资源管理器中双击,下面的单行将暂停脚本。我将它添加到脚本的末尾以保持命令行窗口打开:

if /i "%comspec% /c ``%~0` `" equ "%cmdcmdline:"=`%" pause

【讨论】:

  • 我希望“附录”一班轮工作,但它不适合我。我找到了一个解决方案,但无法在 cmets 中发布,因为降价处理会吃掉反引号。我将它添加到这个要点:gist.github.com/maphew/9f6ba713d30eb1d0f67e7f0b2a338ba5
  • @mattwilkie 如果您发现答案有问题并且确定您的要点已修复,那么请继续编辑答案
  • 谢谢西蒙。发布后,我发现了两种解决方案都不起作用的情况。我仍在摸不着头脑,试图理解为什么,因为我正在测试的环境应该是相同的,但从行为来看,它们显然不是。
  • @mattwilkie 内部cmdcmdline 可能实际上并不是从comspec 构建的,而且我的假设和测试是错误的。我有一种预感,在你提出反例之前没有找到反例。我们也可以完全去掉引号,这可能会导致额外的误报(虽然不能真正看到),但它几乎肯定会降低误报。这对您的情况有用吗? if /i "%comspec% /c %~0 " equ "%cmdcmdline:"=%"
【解决方案5】:

使用exit /b 0,而不是exit

如果从 Windows 资源管理器启动,前者会一直退出,但如果从命令行启动,则会返回控制台。

【讨论】:

    【解决方案6】:

    您可以在从 CMD 窗口运行时添加命令行参数,当双击文件时该参数将不存在。如果没有参数,关闭窗口。如果有,请不要关闭它。可以使用%1测试参数

    【讨论】:

    • 或者您可以将参数放在批处理文件的快捷方式上,并让用户双击快捷方式而不是实际的批处理文件。这样,如果您忘记了参数,就不会意外丢失命令窗口和所有历史记录。
    • @mellamokb:我也想过,但是 OP 专门询问了双击批处理文件,所以我没有建议使用快捷方式。在我原来的答案之后,我可能应该有一个替代方案。谢谢。
    • 谢谢。如果 %cmdcmdline% 解决方案失败,我将继续这样做。在您的解决方案中,当我在 cmd 窗口中时,我需要记住将一个虚拟参数传递给 bat。
    • @KenWhite 当文件被拖到 Windows 资源管理器中的批处理文件上时,这将失败,因为此时第一个参数不会为空。
    • @mrt:如果命令行参数是唯一的,则不会。 /IwasrunfromCMD 效果很好。因此,您改为检查 特定 命令行参数。
    【解决方案7】:

    这不仅是可能的,而且你想要的行为是批处理文件执行的正常行为,除非你做了一些“特殊”的事情:

    • 在资源管理器中通过双击执行批处理文件时,完成后cmd窗口将关闭;
    • 当批处理文件从命令行执行时,完成后它只是简单地返回命令行提示符——窗口没有关闭;

    所以我认为需要回答的问题是,你在批处理文件中做了什么导致命令行执行时关闭命令窗口?

    【讨论】:

      【解决方案8】:

      和@anishsane 一样,如果从资源管理器启动,我也想要一个暂停语句,但从命令窗口启动时不需要。

      根据上面@mousio 的回答,这对我有用:

      @SET cmdcmdline|FINDSTR /b "cmdcmdline="|FINDSTR /i pushd >nul
      @IF ERRORLEVEL 1 (
          @echo.
          @echo Press ENTER when done
          @pause > nul
      )
      

      (这里没有原创,只是提供一个工作示例)

      【讨论】:

        【解决方案9】:

        将此粘贴​​在 BAT 或 CMD 脚本的开头,并可能更改“if”子句中发生的情况:

        :: To leave command window open if script run from Windows explorer.
        @setlocal
        @set x=%cmdcmdline:"=%
        @set x=%x: =%
        @set y=%x:cmd/c=%
        @if "%x%" neq "%y%" cmd /k %0 %* && exit || exit
        @endlocal
        

        它的作用是,如果用户双击或使用“cmd /c”调用此脚本,它将使用“cmd /k”重新启动,这将使会话在命令完成后保持打开状态。这允许用户退出或做其他事情。

        以这种方式而不是在此答案中解释的其他方式这样做的原因是因为我发现即使使用引号或其他符号,IF 语句也会在某些情况下出现 QUOTES 和/c 和空格。所以逻辑首先删除所有引号,然后删除所有空格..因为有时在删除引号后会有一个额外的空格。

        set x=%cmdcmdline:"=%       <-- removes all quotes
        set x=%x: =%                <-- removes all spaces
        set y=%x:cmd/c=%            <-- removes  cmd/c  from the string saving it to  y
        

        && 退出点 || exit 是这样,如果退出前的 ERRORLEVEL 为 0(成功)则停止运行,但如果它不是 0(某些失败)也停止运行。

        但是你可以替换这部分:

        cmd /k %0 %* && exit || exit
        

        类似的东西

        set CALLED_WITH_CMD_C=YES
        

        然后在脚本的其余部分中弥补您自己的差异。然后您必须移动或移除 endlocal。

        前面的'@'符号只是防止回声,如果你想测试,你可以拥有它。 不要使用 echo on 或 echo off,因为它会更改设置并影响调用您的所有后续脚本。

        【讨论】:

          【解决方案10】:

          @dlchambers 很接近但 set 不起作用,因为 cmdcmdline 在某些情况下不是定义的环境变量,但这个基于他的版本对我来说非常有用:

          echo %cmdcmdline% | findstr /i pushd >nul
          if errorlevel 1 pause
          

          【讨论】:

            【解决方案11】:

            在阅读了这些建议后,这就是我的想法:

            set __cmdcmdline=%cmdcmdline%
            set __cmdcmdline=%__cmdcmdline:"=%
            set __cmdcmdline=%__cmdcmdline: =%
            set __cmdcmdline=%__cmdcmdline:~0,5%
            if "%__cmdcmdline%"=="cmd/c" set CMD_INITIATED_FROM_EXPLORER=1
            set __cmdcmdline=
            

            有条件地设置变量:CMD_INITIATED_FROM_EXPLORER

            ..然后可以根据需要使用:

            if defined CMD_INITIATED_FROM_EXPLORER (
              echo.
              pause
            )
            

            ..但是@Ruben Bartelink 提到的有关 Powershell 的问题没有解决:

            从 Powershell 运行 ./batch.cmd 在后台使用 cmd /c

            【讨论】:

              【解决方案12】:

              您还可以检查SESSIONNAME 环境变量。

              正如您在此处看到的,该变量通常不在资源管理器窗口中设置。从 cmd 调用时,SESSIONNAME 设置为 Console。我可以在 Windows 10 上确认这一点。

              不幸的是,行为似乎是多变的:https://support.microsoft.com/de-de/help/2509192/clientname-and-sessionname-enviroment-variable-may-be-missing

              【讨论】:

                【解决方案13】:

                (部分)相反,除了接受的答案 AToW(re %cmdcmdline%)和 Win10 中的最佳答案 AToW(re if /i %0 equ "%~dpnx0")之外,它是:

                • 在 CMD 中:

                  • *.cmd(这里是_pauseIfRunFromGUI.cmd):

                    "C:\Windows\System32\cmd.exe"
                    
                    if /I _pauseIfRunFromGUI[.cmd] EQU "C:\Users\Geri\_pauseIfRunFromGUI.cmd"
                    

                    .cmd 如果在 cmd 行中输入,则会出现,如果您使用 Tab 完成,则会发生这种情况。

                  • *.cmd (_pauseIfRunFromGUI.cmd) 中,call*.cmd 编辑:

                    "C:\Windows\System32\cmd.exe"
                    
                    if /I _pauseIfRunFromGUI[.cmd] EQU "C:\Users\Geri\_pauseIfRunFromGUI.cmd"
                    

                    同上。

                    如果通过call _pauseIfRunFromGUI.cmd 调用,则存在.cmd

                  在任何情况下,比较的结果都是预期的 false

                • 来自 GUI:

                  (桌面上的资源管理器和链接)

                  • 在从 GUI 启动的 *.cmd(此处为 _pauseIfRunFromGUI.cmd)中:

                    C:\WINDOWS\system32\cmd.exe /c ""C:\Users\Geri\_pauseIfRunFromGUI.cmd" "
                    
                    if /I "C:\Users\Geri\_pauseIfRunFromGUI.cmd" EQU "C:\Users\Geri\_pauseIfRunFromGUI.cmd"
                    

                    这个与接受的答案 AToW 不同,后者只是说 cmd /c ""..." "_!

                    比较结果为 true,这是预期的。

                  • call 编辑的 *.cmd (_pauseIfRunFromGUI.cmd) 中,由从 GUI 启动的 *.cmd(此处为 calling.cmd):

                    C:\WINDOWS\system32\cmd.exe /c ""C:\Users\Geri\calling.cmd" "
                    
                    if /I _pauseIfRunFromGUI[.cmd] EQU "C:\Users\Geri\_pauseIfRunFromGUI.cmd"
                    

                    与上述不同,因为calling .cmdcmdcmdline 中,当然,而不是在其中评估它的那个(_pauseIfRunFromGUI.cmd)。

                    如果通过call _pauseIfRunFromGUI.cmdcalling.cmd 内调用,则存在.cmd

                    比较结果为 false,这是 预期的!

                如果比较改为:

                if /i "%cmdcmdline:~0,31%"=="C:\WINDOWS\system32\cmd.exe /c " echo: & pause
                

                一切都按预期进行。

                【讨论】:

                  猜你喜欢
                  • 2020-01-23
                  • 2022-11-22
                  • 2021-01-20
                  • 1970-01-01
                  • 2021-09-27
                  • 2014-04-07
                  • 2018-04-27
                  • 2021-12-24
                  • 1970-01-01
                  相关资源
                  最近更新 更多