开发者

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 b开发者_Python百科at file from explorer. 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

if (initiated_from_explorer) then
else
endif

Is this possible ? thanks


mousio's solution is nice but I 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%).

In constrast, the solution using %0 works fine. 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):

IF %0 EQU "%~dpnx0" PAUSE

However, note that this solution with %~dpnx0 fails when

  1. the .bat file is located somewhere in the %USERPROFILE% directory, and
  2. your %USERNAME% contains one or more uppercase characters

because... wait for it... the d in %~dpnx0 forces your %USERPROFILE% username to lowercase, while plain %0 does not. So they're never equal if your username contains an uppercase character. ¯\_(ツ)_/¯

[Edit 18 June 2021 - thanks to JasonXA]

You can solve this lowercase issue with case-insensitive comparison (magic /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.

  • When launched from a command console, this var is "%SystemRoot%\system32\cmd.exe".
  • When launched from explorer this var is 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.

Personally, I would go for the %cmdcmdline% approach (not %O), but be aware that both start commands can be overridden in the registry…


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.
  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).
  3. The if statement sees if "testl" and "testr" are different. If yes, batch was double-clicked, so pause; if no, batch was typed in on command line, go on.

Naturally, if you want to do something else if you detect a double-click, you can change the pause.

Thanks everyone.


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.
  2. Recreate the expected cmdcmdline exactly (well, with '"' replaced by 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). If cmdcmdline isn't found, it correctly renders 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:

(Edit 2022-01-12, fixed quote mismatching from this discussion)

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


Use exit /b 0, not exit

The former will exit all the way if launched from Windows Explorer, but return to the console if launched from the command line.


You can add a command line parameter when running from a CMD window that won't exist when the file is double-clicked. If there is no parameter, close the window. If there is, don't close it. You can test the parameter using %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;
  • 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;

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?


Like @anishsane I too wanted a pause statement if launched from explorer, but not when launched from a command window.

Here's what worked for me, based upon @mousio's answer above:

@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:

:: 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. 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. 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.

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. You would have to then move or remove the 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.


@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:

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

..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:

running ./batch.cmd from Powershell uses cmd /c under the hood


You also can check for SESSIONNAME environment variable.

As you see here that variable typically isn't set in Explorer window. When invoking from cmd it SESSIONNAME is set to Console. I can confirm this for Windows 10.

Unfortunately behaviour seems to be changeable: 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:

  • in CMD:

    • in a *.cmd (here _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.

    • in a *.cmd (_pauseIfRunFromGUI.cmd) that's called by a *.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.

    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:

      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 ""..." "_!

      The comparison evaluates to true which is intended.

    • in a *.cmd (_pauseIfRunFromGUI.cmd) that's called 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).

      .cmd is present if called via call _pauseIfRunFromGUI.cmd within calling.cmd.

      The comparison evaluates to false which is not intended!

If the comparison is changed to:

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

everything works as expected.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜