简体   繁体   English

通过双击或从 cmd window 检测 bat 文件是否正在运行

[英]Detect if bat file is running via double click or from cmd window

I have a bat file that does a bunch of things and closes the cmd window which is fine when user double clicks the bat file from explorer.我有一个 bat 文件,它做了很多事情并关闭 cmd window 当用户从资源管理器中双击 bat 文件时这很好。 But if I run the bat file from a already open cmd window as in cmd>c:\myfile.bat then I do not want the bat file to close the cmd window (END) since I need to do other things. But if I run the bat file from a already open cmd window as in cmd>c:\myfile.bat then I do not want the bat file to close the cmd window (END) since I need to do other things. I need bat dos command code that will do something like我需要 bat dos 命令代码来执行类似的操作

if (initiated_from_explorer) then
else
endif

Is this possible?这可能吗? thanks谢谢

mousio's solution is nice; mousio 的解决方案很好; however, I personally did not manage to make it work in an "IF" statement because of the double quotes in the value of %cmdcmdline% (with or without double quotes around %cmdcmdline% ).但是,我个人没有设法使其在“IF”语句中工作,因为%cmdcmdline%的值中有双引号(在%cmdcmdline%周围有或没有双引号)。

In constrast, the solution using %0 works fine.相反,使用%0的解决方案工作正常。 I used the following block statement and it works like a charm:我使用了以下块语句,它就像一个魅力:

IF %0 == "%~0"  pause

The following solution, which expands %~0 to a fully qualified path, might also work if the previous does not (cf. Alex Essilfie's comment ):以下解决方案将%~0扩展为完全限定的路径,如果前一个没有(参见Alex Essilfie 的评论),也可能有效:

IF %0 EQU "%~dpnx0" PAUSE

However, note that this solution with %~dpnx0 fails when但是,请注意,使用%~dpnx0的解决方案在以下情况下会失败

  1. the.bat file is located somewhere in the %USERPROFILE% directory, and .bat 文件位于%USERPROFILE%目录中的某个位置,并且
  2. your %USERNAME% contains one or more uppercase characters您的%USERNAME%包含一个或多个大写字符

because... wait for it... the d in %~dpnx0 forces your %USERPROFILE% username to lowercase, while plain %0 does not.因为...等待它... %~dpnx0中的d强制您的%USERPROFILE%用户名小写,而普通的%0不会。 So they're never equal if your username contains an uppercase character.因此,如果您的用户名包含大写字符,它们永远不会相等。 ¯\_(ツ)_/¯

[Edit 18 June 2021 - thanks to JasonXA] [编辑 2021 年 6 月 18 日 - 感谢 JasonXA]

You can solve this lowercase issue with case-insensitive comparison (magic /I ):您可以通过不区分大小写的比较(魔术/I )解决这个小写问题:

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

This might be the best solution of all!这可能是最好的解决方案!

%cmdcmdline% gives the exact command line used to start the current Cmd.exe. %cmdcmdline%给出了用于启动当前 Cmd.exe 的确切命令行。

  • When launched from a command console, this var is "%SystemRoot%\system32\cmd.exe" .从命令控制台启动时,此 var 为"%SystemRoot%\system32\cmd.exe"
  • When launched from explorer this var is cmd /c ""{full_path_to_the_bat_file}" " ;当从资源管理器启动时,这个变量是cmd /c ""{full_path_to_the_bat_file}" " ;
    this implicates that you might also check the %0 variable in your bat file, for in this case it is always the full path to the bat file, and always enclosed in double quotes.这意味着您可能还会检查 bat 文件中的%0变量,因为在这种情况下,它始终是 bat 文件的完整路径,并且始终用双引号括起来。

Personally, I would go for the %cmdcmdline% approach (not %O ), but be aware that both start commands can be overridden in the registry…就个人而言,我会 go 用于%cmdcmdline%方法(不是%O ),但请注意,两个启动命令都可以在注册表中被覆盖......

A consolidated answer, derived from much of the information found on this page:综合答案,源自此页面上的大部分信息:

:pauseIfDoubleClicked
setlocal enabledelayedexpansion
set testl=%cmdcmdline:"=%
set testr=!testl:%~nx0=!
if not "%testl%" == "%testr%" pause
  1. The variable "testl" gets the full line of the cmd processor call (as per mousio), stripping out all of the pesky double quotes.变量“testl”获取 cmd 处理器调用的完整行(根据 mousio),去掉所有讨厌的双引号。
  2. The variable "testr" takes "testl" and further strips outs the name of the current batch file name if present (which it will be if the batch file was invoked with a double-click).变量“testr”采用“testl”并进一步去除当前批处理文件的名称(如果存在)(如果批处理文件是通过双击调用的)。
  3. The if statement sees if "testl" and "testr" are different. if 语句查看“testl”和“testr”是否不同。 If yes, batch was double-clicked, so pause;如果是,批处理被双击,所以暂停; if no, batch was typed in on command line, go on.如果否,则在命令行上输入批处理,打开 go。

Naturally, if you want to do something else if you detect a double-click, you can change the pause.自然,如果您想在检测到双击时执行其他操作,则可以更改暂停。

Thanks everyone.感谢大家。

Use exit /b 0 , not exit使用exit /b 0 ,而不是exit

The former will exit all the way if launched from Windows Explorer, but return to the console if launched from the command line.如果从 Windows Explorer 启动,前者将一直退出,但如果从命令行启动,则返回控制台。

You can add a command line parameter when running from a CMD window that won't exist when the file is double-clicked.从 CMD window 运行时,您可以添加命令行参数,该参数在双击文件时将不存在。 If there is no parameter, close the window.如果没有参数,关闭window。 If there is, don't close it.如果有,请不要关闭它。 You can test the parameter using %1您可以使用%1测试参数

It's not only possible, but your desired behavior is the normal behavior of batch file execution, unless you do something 'special':这不仅是可能的,而且您想要的行为是批处理文件执行的正常行为,除非您做一些“特殊”的事情:

  • when executing a batch file by double-clicking it in Explorer, the cmd window will close when it's done;通过在资源管理器中双击执行批处理文件时,cmd window 将在完成后关闭;
  • when the batch file is executed from the command line, it simply returns to the command line prompt when complete - the window is not closed;当批处理文件从命令行执行时,它只是在完成后返回命令行提示符 - window 未关闭;

So I think the question that needs to be answered is what are you doing in the batch file that causes the command window to close when you execute it by the command line?所以我认为需要回答的问题是你在批处理文件中做了什么导致命令 window 在你通过命令行执行它时关闭?

Less code, more robust:更少的代码,更健壮:

Build upon the other answers, I find the most robust approach to:在其他答案的基础上,我找到了最可靠的方法:

  1. Replace quotes with x to enable text comparison without breaking the IF statement.x替换引号以启用文本比较而不破坏 IF 语句。
  2. Recreate the expected cmdcmdline exactly (well, with '"' replaced by x ).准确地重新创建预期的 cmdcmdline(好吧,用 '"' 替换x )。
  3. Test for case-insensitive equality.测试不区分大小写的相等性。

The result is:结果是:

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

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

This adds more robustness against general cmd /c calls, since Explorer adds an awkward extra space before the last quote/x (in our favor).这增加了对一般cmd /c调用的鲁棒性,因为 Explorer 在最后一个引号 /x 之前添加了一个尴尬的额外空间(对我们有利)。 If cmdcmdline isn't found, it correctly renders isdoubleclicked=0 .如果cmdcmdline ,它会正确呈现isdoubleclicked=0

Addendum:附录:

Similarly to the above method, the following one-liner will pause a script if it was double-clicked from explorer.与上述方法类似,如果从资源管理器中双击脚本,则以下单行将暂停脚本。 I add it to the end of my scripts to keep the command-line window open:我将它添加到脚本的末尾以保持命令行 window 处于打开状态:

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

Like @anishsane I too wanted a pause statement if launched from explorer, but not when launched from a command window.像@anishsane 一样,如果从资源管理器启动,我也想要一个暂停语句,但不是从命令 window 启动时。

Here's what worked for me, based upon @mousio's answer above:根据上面@mousio的回答,这对我有用:

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

(Nothing original here, just providing a working example) (这里没有原创,只是提供一个工作示例)

Paste this at the beginning of your BAT or CMD script and maybe change what happens in the 'if' clause:将此粘贴到 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

What this does, is if the user either double-clicks or calls this script using "cmd /c" it will re-launch with "cmd /k" which will leave the session open after the command finishes.这样做的目的是,如果用户双击或使用“cmd /c”调用此脚本,它将使用“cmd /k”重新启动,这将使 session 在命令完成后保持打开状态。 This allows the user to EXIT or maybe do something else.这允许用户退出或做其他事情。

The reason for doing it this way rather than the other ways explained in this answer is because I've found situations that still even with using the quotes or other symbols, the IF statement would barf with certain situations of the QUOTES and the /c and with spaces.这样做的原因而不是这个答案中解释的其他方式是因为我发现即使使用引号或其他符号,IF 语句也会在某些情况下出现 QUOTES 和 /c 和带空格。 So the logic first removes all QUOTES and then removes all spaces.. because SOMETIMES there is an extra space after removing the quotes.所以逻辑首先删除所有引号,然后删除所有空格..因为有时在删除引号后会有一个额外的空格。

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

The point of the && exit || && 退出点|| exit is so that if the ERRORLEVEL before exiting is 0 (success) it then stops running, but also if it is non 0 (some failure) it also stops running. exit 是这样的,如果退出前的 ERRORLEVEL 为 0(成功),则它会停止运行,但如果它不是 0(某些失败),它也会停止运行。

But you can replace this part:但是你可以替换这部分:

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

with something like有类似的东西

set CALLED_WITH_CMD_C=YES

and then make up your own differences in the rest of your script.然后在脚本的 rest 中弥补自己的差异。 You would have to then move or remove the endlocal.然后,您必须移动或删除 endlocal。

The '@' symbol at front just prevents the echo, which you can have if you want to test.前面的“@”符号只是防止回声,如果你想测试,你可以拥有它。 Do not use echo on or echo off as it changes the setting and affects all subsequent scripts that call yours.不要使用 echo on 或 echo off,因为它会更改设置并影响调用您的所有后续脚本。

@dlchambers was close but set didn't work since cmdcmdline isn't a defined environment variable in some cases, but this version based on his works great for me: @dlchambers 很接近但 set 不起作用,因为 cmdcmdline 在某些情况下不是定义的环境变量,但这个基于他的版本对我来说非常有用:

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

after reading through the suggestions, this is what I went with:在阅读了建议后,这就是我的想法:

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=

which conditionally sets the variable: CMD_INITIATED_FROM_EXPLORER有条件地设置变量: CMD_INITIATED_FROM_EXPLORER

..and can subsequently be used as needed: ..然后可以根据需要使用:

if defined CMD_INITIATED_FROM_EXPLORER (
  echo.
  pause
)

..but the issue regarding Powershell that @Ruben Bartelink mentions isn't solved: ..但是@Ruben Bartelink提到的关于Powershell的问题没有解决:

running ./batch.cmd from Powershell uses cmd /c under the hood从 Powershell 运行./batch.cmd在引擎盖下使用cmd /c

You also can check for SESSIONNAME environment variable.您还可以检查SESSIONNAME环境变量。

As you see here that variable typically isn't set in Explorer window.正如您在此处看到的,该变量通常未在 Explorer window 中设置。 When invoking from cmd it SESSIONNAME is set to Console .从 cmd 调用时,它SESSIONNAME设置为Console I can confirm this for Windows 10.我可以为 Windows 10 确认这一点。

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

(Partly) Contrary and in addition to the accepted answer AToW (re %cmdcmdline% ) and the top answer AToW (re if /i %0 equ "%~dpnx0" ) in Win10 it is: (部分)相反,除了在 Win10 中接受的答案 AToW (re %cmdcmdline% ) 和最佳答案 AToW (re if /i %0 equ "%~dpnx0" ) 它是:

  • in CMD:在 CMD 中:

    • in a *.cmd (here _pauseIfRunFromGUI.cmd ):*.cmd (此处_pauseIfRunFromGUI.cmd ):

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

      .cmd is present if entered on the cmd line, which happens if you complete with Tab .如果在.cmd行中输入,则会出现 .cmd ,如果您使用Tab完成,则会发生这种情况。

    • in a *.cmd ( _pauseIfRunFromGUI.cmd ) that's call ed by a *.cmd :在由 *.cmd call*.cmd *.cmd ( _pauseIfRunFromGUI.cmd ) 中:

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

      Same as above.和上面一样。

      .cmd is present if called via call _pauseIfRunFromGUI.cmd .如果通过调用.cmd调用,则存在call _pauseIfRunFromGUI.cmd

    In any way the comparison evaluates to false which is intended.无论如何,比较评估为,这是预期的。

  • from GUI:从图形用户界面:

    (Explorer and link on Desktop) (桌面上的资源管理器和链接)

    • in a *.cmd (here _pauseIfRunFromGUI.cmd ) that's launched from the 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"

      This one is different to the accepted answer AToW which says just cmd /c ""..." " _!与接受的答案 AToW 不同,后者仅说cmd /c ""..." " _!

      The comparison evaluates to true which is intended.比较评估结果为true ,这是预期的。

    • in a *.cmd ( _pauseIfRunFromGUI.cmd ) that's call ed by a *.cmd (here calling.cmd ) that's launched from the GUI: in a *.cmd ( _pauseIfRunFromGUI.cmd ) that's call ed by a *.cmd (here calling.cmd ) that's launched from the GUI:

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

      Different to above, since calling.cmd is in cmdcmdline , of course, not the one in which it is evaluated ( _pauseIfRunFromGUI.cmd ).与上述不同,由于calling.cmd是在cmdcmdline中,当然,不是在其中评估它的那个( _pauseIfRunFromGUI.cmd )。

      .cmd is present if called via call _pauseIfRunFromGUI.cmd within calling.cmd .如果通过call _pauseIfRunFromGUI.cmd.cmd中调用,则存在calling.cmd

      The comparison evaluates to false which is not intended!比较结果为,这不是故意的!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM