views:

1758

answers:

4

I have a DOS batch script that invokes a java application which interacts with the user through the console UI. For the sake of argument, let's call it runapp.bat and its contents be

java com.example.myApp

If the batch script is invoked in a console, everything works fine. However, if the script is invoked from the Window Manager, the newly opened console closes as soon as the application finishes executing. What I want is for the console to stay open in all cases.

I know of the following tricks:

  • add a pause command at the end of the script. This is a bit ugly in case runapp.bat is invoked from the command line.

  • create a new shell using cmd /K java com.example.myApp This is the best solution I found so far, but leaves an extra shell environment when invoked from the command line, so that calling exit doesn't actually close the shell.

Is there a better way?

+2  A: 

See this question: Detecting how a batch file was executed

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="

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

:: Run your app
java com.example.myApp

:EXIT
if defined PAUSE_ON_CLOSE pause
Patrick Cuff
+2  A: 

I prefer using %cmdcmdline% as posted in the comment to Patrick's answer to the other question (which I didn't find although looked). That way, even if someone decides to use quotes to call the batch script, it won't trigger the false positive.

My final solution:

@echo off
java com.example.myApp %1 %2

REM "%SystemRoot%\system32.cmd.exe"   when from console
REM cmd /c ""[d:\path\script.bat]" "  when from windows explorer

@echo %cmdcmdline% | findstr /l "\"\"" >NUL
if %ERRORLEVEL% EQU 0 pause
ykaganovich
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
A: 
cmd /K java com.example.myApp & pause & exit

will do the job. The & will execute the command one after another. If you use && you can break if one fails.

Totonga