tags:

views:

654

answers:

7

Assuming Windows, is there a way I can detect from within a batch file if it was launched from an open command prompt or by double-clicking? I'd like to add a pause to the end of the batch process if and only if it was double clicked, so that the window doesn't just disappear along with any useful output it may have produced.

Any clever ways to do this? I'm looking for solutions I could rely on to work on a machine that was configured more or less with default settings.

A: 

Just add pause regardless of how it was opened? If it was opened from command prompt no harm done apart from a harmless pause. (Not a solution but just thinking whether a pause would be so harmful / annoying )

Learning
I think that would be pretty annoying, at least for me. Surely there has to be some environment variable or state setup that exists in one place and not the other I could use to make the choice!
Dan Olson
+4  A: 

Don't overlook the solution of having two batch files:
abatfile.bat and abatfile-with-pause.bat
The second simply calling the first and adding a pause

hamishmcn
But how to find which one to call? I think that brings us back to the original question?
Learning
Hi Learning. If they are running from the command line they run the one with out the pause (because the output will still be visible), and if they run by double clicking then they double click one the one with the pause so that they can see the results and then dismiss the window
hamishmcn
A: 

Similar to a second batch file you could also pause if a certain parameter is not given (called via clicking).

This would mean only one batch file but having to specify a -nopause parameter or something like that when calling from the console.

mxp
This is about the best manual solution, but I am really hoping for an automatic one. Perhaps branching on the current directory, which may be constant when double clicked. Ideally something with minimal setup that will work everywhere without screwing with shortcuts.
Dan Olson
A: 

crazy idea: use tasklist and parse it's results. I've wrote in a test batch file:

tasklist > test.out

and when I double-clicked it, there was an additional "cmd.exe" process just before the tasklist process, that wasn't there when the script was run from command line (but note that might not be enough if someone opens a command line shell and then double-click the batch file)

+4  A: 

I just ran a quick test and noticed the following, which may help you:

  • When run from an open command prompt, the %0 variable does not have double quotes around the path. If the script resides in the current directory, the path isn't even given, just the batch file name.
  • When run from explorer, the %0 variable is always enclosed in double quotes and includes the full path to the batch file.

This script will not pause if run from the command console, but will if double-clicked in Explorer:

@echo off
setlocal enableextensions

set SCRIPT=%0
set DQUOTE="

@echo do something...

@echo %SCRIPT:~0,1% | findstr /l %DQUOTE% > NUL
if %ERRORLEVEL% EQU 0 set PAUSE_ON_CLOSE=1

:EXIT
if defined PAUSE_ON_CLOSE pause

EDIT: There was also some weird behavior when running from Explorer that I can't explain. Originally, rather than

@echo %SCRIPT:~0,1% | findstr /l %DQUOTE% > NUL
if %ERRORLEVEL% EQU 0 set PAUSE_ON_CLOSE=1

I tried using just an if:

if %SCRIPT:0,1% == ^" set PAUSE_ON_CLOSE=1

This would work when running from an open command prompt, but when run from Explorer it would complain that the if statement wasn't correct.

Patrick Cuff
Another option: %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 ""[d:\path\script.bat]" "
Patrick Cuff
+1  A: 

I use a parameter "automode" when I run my batch files from scripts.

set automode=%7

(Here automode is the seventh parameter given.)

Some code follows and when the file should pause, I do this:

if @%automode%==@ pause
Andrew J. Brehm
A: 

I frequently use alternate shells (primarily TCC/LE from jpsoft.com) and subshells. I've found that this code works for a wider, more general case (and it doesn't require FINDSTR):

@echo off & setlocal
if "%CMDEXTVERSION%"=="" ( echo REQUIRES command extensions & exit /b 1 ) &:: REQUIRES command extensions for %cmdcmdline% and %~$PATH:1 syntax

call :_is_similar_command _FROM_CONSOLE "%COMSPEC%" %cmdcmdline%
if "%_PAUSE_NEEDED%"=="0" ( goto :_START )
if "%_PAUSE_NEEDED%"=="1" ( goto :_START )
set _PAUSE_NEEDED=0
if %_FROM_CONSOLE% equ 0 ( set _PAUSE_NEEDED=1 )
goto :_START
::
:_is_similar_command VARNAME FILENAME1 FILENAME2
:: NOTE: not _is_SAME_command; that would entail parsing PATHEXT and concatenating each EXT for any argument with a NULL extension
setlocal
set _RETVAL=0
:: more than 3 ARGS implies %cmdcmdline% has multiple parts (therefore, NOT direct console execution)
if NOT [%4]==[] ( goto :_is_similar_command_RETURN )
:: deal with NULL extensions (if both NULL, leave alone; otherwise, use the non-NULL extension for both)
set _EXT_2=%~x2
set _EXT_3=%~x3
if NOT "%_EXT_2%"=="%_EXT_3%" if "%_EXT_2%"=="" (
 call :_is_similar_command _RETVAL "%~2%_EXT_3%" "%~3"
 goto :_is_similar_command_RETURN
 )
if NOT "%_EXT_2%"=="%_EXT_3%" if "%_EXT_3%"=="" (
 call :_is_similar_command _RETVAL "%~2" "%~3%_EXT_2%"
 goto :_is_similar_command_RETURN
 )
::if /i "%~f2"=="%~f3" ( set _RETVAL=1 )  &:: FAILS for shells executed with non-fully qualified paths (eg, subshells called with 'cmd.exe' or 'tcc')
if /i "%~$PATH:2"=="%~$PATH:3" ( set _RETVAL=1 )
:_is_similar_command_RETURN
endlocal & set "%~1=%_RETVAL%"
goto :EOF
::

:_START

if %_FROM_CONSOLE% EQU 1 (
 echo EXEC directly from command line
  ) else (
 echo EXEC indirectly [from explorer, dopus, perl system call, cmd /c COMMAND, subshell with switches/ARGS, ...]
 )
if %_PAUSE_NEEDED% EQU 1 ( pause )

Initially, I had used if /i "%~f2"=="%~f3" in the _is_similar_command subroutine. The change to if /i "%~$PATH:2"=="%~$PATH:3" and the additional code checking for NULL extensions allows the code to work for shells/subshells opened with non-fully qualified paths (eg, subshells called with just 'cmd.exe' or 'tcc').

For arguments without extensions, this code does not parse and use the extensions from %PATHEXT%. It essentially ignores the hierarchy of extensions that CMD.exe uses when searching for a command without extension (first attempting FOO.com, then FOO.exe, then FOO.bat, etc.). So, _is_similar_command checks for similarity, not equivalence, between the two arguments as shell commands. This could be a source of confusion/error, but will, in all likelyhood, never arise as a problem in practice for this application.

Edit: Initial code was an old version. The code is now updated to the most recent version which has: (1) a swapped %COMSPEC% and %cmdcmdline% in the initial call, (2) added a check for multiple %cmdcmdline% arguments, (3) echoed messages are more specific about what is detected, and (4) a new variable %_PAUSE_NEEDED% was added.

It should be noted that %_FROM_CONSOLE% is set based specifically on whether the batch file was excecuted directly from the console command line or indirectly through explorer or some other means. These "other means" can include a perl system() call or by executing a command such as cmd /c COMMAND.

The variable %_PAUSE_NEEDED% was added so that processes (such as perl) which execute the batch file indirectly can bypass pauses within the batch file. This would be important in cases in which output is not piped to the visible console (eg, perl -e "$o = qx{COMMAND}"). If a pause occurs in such a case, the "Press any key to continue . . ." pause prompt would never be displayed to the user and the process will hang waiting for unprompted user input. In instances where user interaction is either not possible or not allowed, the %_PAUSE_NEEDED% variable can be preset to "0" or "1" (false or true respectively). %_FROM_CONSOLE% is still set correctly by the code, but the value of %_PAUSE_NEEDED% is not subsequently set based upon %_FROM_CONSOLE%. It is just passed through.

And also note that the code will incorrectly detect execution as indirect (%_FROM_CONSOLE%=0) within a subshell if that subshell is opened with a command containing switches/options (eg, cmd /x). Generally this isn't a big problem as subshells are usually opened without extra switches and %_PAUSE_NEEDED% can be set to 0, when necessary.

Caveat codor.

Roy