Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make windows batch file pause when double-clicked?

I've written a batch file to automate some tasks. I can run it from a command window and it runs and displays results. If I double click it from explorer though, it runs and terminates immediately so I can't see the results.

Is there a way I can make batch file window stay open until I dismiss it if I started it by double-clicking the icon?

I don't want to have to pass a /nopause parameter or something when I call a batch file from the command line. I'd like a solution where I can use the batch file without having to do anything too special?

Thanks.

NOTE I don't want it to pause when running from the command line!! I may call this batch file from another batch file to carry out a load of operations. In that case I can't be sitting there to keep hitting enter.

Ideally it would be best if I can put some code in the batch file so it can work out where it was started from, and then pause or not as appropriate.

like image 593
Scott Langham Avatar asked May 20 '09 08:05

Scott Langham


3 Answers

Use:

cmd /K myBatch.bat

as your shortcut.

like image 126
Matthew Flaschen Avatar answered Nov 18 '22 11:11

Matthew Flaschen


Here is a solution that should work well and take into consideration the possibility that a batch-file might call another batch-file ("Nested").

You could use Find, to look for "/c" which should not be present if the batch-file is run from a "Command Prompt":

echo %cmdcmdline% | find /i "/c"

But, you could do a more "robust" test by using Find to search for a longer string, or the batch-file name.

The "Find" command will not work properly if the search string has (") double-quotes within it. To work around that, you can use environment variable substitution to "adjust" the string so it plays nice with Find:

set newcmdcmdline=%cmdcmdline:"=-%

This will typically return:

if the batch-file is run from a "Command Prompt"  
    newcmdcmdline=-C:\Windows\system32\cmd.exe-

if the batch-file is run by clicking on the the batch-file  
(or the batch-file shortcut) from "Windows Explorer"  
    newcmdcmdline=cmd /c --D:\Path\test.cmd- -

Then you can use "Find" to test like:

    echo %newcmdcmdline% | find /i "cmd /c --"
or
    echo %newcmdcmdline% | find /i "cmd /c --%~dpf0%-"

Next, you need to decide if you want a "Nested" batch-file to behave as if you executed it in the same way as the calling batch-file, or if you want nested batch-files to always behave as if they were executed from a "Command Prompt".

Consider that if you are running in a nested batch-file, then testing for this:

echo %newcmdcmdline% | find /i "cmd /c --%~dpf0%-"

will always fail (no match) because %newcmdcmdline% contains the name of the outermost batch-file, not the nested batch-file.

So the first solution will behave the same for the calling batch-file, and all nested batch-files. Also perfect if you don't call any batch-files:

In all batch-files (calling and nested) that you care to make this test, add these lines, typically near the top of the batch-files (you may exclude the echo-statements if you wish):

if not defined withincmd call :testshell
if %withincmd% EQU 0 echo This batch-file: %~dpf0 was executed directly (from Windows Explorer, ...).
if %withincmd% EQU 1 echo This batch-file: %~dpf0 was executed from within a Command Prompt
rem if %withincmd% EQU 0 pause

Then, somewhere within each batch-file, add the testshell sub-function:

goto :EOF
:testshell
rem "Nested" batch-files won't see this because withincmd is already defined
if not defined newcmdcmdline set newcmdcmdline=%cmdcmdline:"=-%
set withincmd=1
echo %newcmdcmdline% | find /i "cmd /c --%~dpf0%-"
if %errorlevel% EQU 0 set withincmd=0
goto :EOF

You only make the conditional call to "testshell" one time, at the top of the outermost batch-file.

In some situations, you may want to have only the "outermost" batch-file behave differently if it is executed from a "Command Prompt" versus if it is run by clicking on the the batch-file (or the batch-file shortcut) from "Windows Explorer". So, batch-files called from the "outermost" batch-file will always behave the same regardless of how they are run.

For this to work, you have a few choices.

1) Save the value of "withincmd" before you call another batch-file, and restore the previous value of "withincmd" after the called batch-file returns. This is a little involved for most cases.

2) Use a "globally-unique" variable name for "withincmd" in each batch-file.

3) Execute the "Find" command each time you need to know how the current batch-file was run.

4) Increment a variable on entry to a batch-file and decrement it on batch-file exit, then only test how batch-file was run if count-variable=1

Method 3 is the easiest, but has the downside that if the outermost batch-file is called from itself (as in recursion) or another batch-file, the test variable (withincmd) will not be properly set.

Here's how to do it using method 3:

In all batch-files (calling and nested) that you care to make this test, add these lines, typically near the top of the batch-files (you may exclude the echo-statements if you wish):

call :testshell
if %withincmd% EQU 0 echo This batch-file: %~dpf0 was executed directly (from Windows Explorer, ...).
if %withincmd% EQU 1 echo This batch-file: %~dpf0 was executed from (or Nested) within a Command Prompt
rem if %withincmd% EQU 0 pause

Then, somewhere within each batch-file, add the testshell sub-function:

goto :EOF
:testshell
if not defined newcmdcmdline set newcmdcmdline=%cmdcmdline:"=-%
set withincmd=1
echo %newcmdcmdline% | find /i "cmd /c --%~dpf0%-"
if %errorlevel% EQU 0 set withincmd=0
goto :EOF

In this case, you have to call "testshell" once, at the top of the EACH batch-file, then again after you have returned from calling another batch-file (or call "testshell" each time you need to know how the current batch-file was run).

Here's how to do it using method 4:

In all batch-files (calling and nested) that you care to make this test, add these lines, typically near the top of the batch-files (you may exclude the echo-statements if you wish):

if not defined nestinglevel set nestinglevel=0
set /A nestinglevel=nestinglevel+1
call :testshell
if %withincmd% EQU 0 echo This batch-file: %~dpf0 was executed directly (from Windows Explorer, ...).
if %withincmd% EQU 1 echo This batch-file: %~dpf0 was executed from (or Nested) within a Command Prompt
rem if %withincmd% EQU 0 pause

Then, somewhere within each batch-file, add the testshell sub-function:

goto :EOF
:testshell
if not defined newcmdcmdline set newcmdcmdline=%cmdcmdline:"=-%
set withincmd=1
if %nestinglevel% GEQ 2 goto :EOF
echo %newcmdcmdline% | find /i "cmd /c --%~dpf0%-"
if %errorlevel% EQU 0 set withincmd=0
goto :EOF

Also, remember to decrement the variable when you exit one batch-file to return to the calling batch-file:

set /A nestinglevel=nestinglevel-1

In this case, you have to call "testshell" once, at the top of the EACH batch-file, then again after you have returned from calling another batch-file (or call "testshell" each time you need to know how the current batch-file was run).

In all cases, test %withincmd% to determine how the current batch-file was run, like this:

if %withincmd% EQU 0 pause
if %withincmd% EQU 1 goto :EOF
like image 8
Kevin Fegan Avatar answered Nov 18 '22 11:11

Kevin Fegan


My problem was similar - I wanted a pause at the very end if called from Windows Explorer, but no pause if in a command window. I came up with this. At the top of every batch file, put this:

if "%parent%"=="" set parent=%~0
if "%console_mode%"=="" (set console_mode=1& for %%x in (%cmdcmdline%) do if /i "%%~x"=="/c" set console_mode=0)

and then at end

if "%parent%"=="%~0" ( if "%console_mode%"=="0" pause )

It handles nested batch calls, where you only want to pause at the end of the original batch file, not in nested batch files. In a nested batch file, %parent% is already set to original caller so it won't equal the nested %~0. If you have bat1 which calls bat2, it leaves open the option of double clicking bat2 in Explorer - in that context bat2 will pause at end, whereas if bat1 calls bat2, then bat2 won't pause at the end (only bat1 will pause).

The & statement separator helps avoid visual clutter for something which is secondary to the main function. If you don't like the look of it, just put each statement on a new line.

This approach looks for /C as one of the params in the launch command (%cmdcmdline%). It assumes your batch files don't use the /C option. If you use /C yourself, then you need to instead check if %COMSPEC%appears within %cmdcmdline% (use FINDSTR). When Explorer launches a bat file, its %cmdcmdline% includes %COMSPEC% eg C:\Windows\System32\cmd.exe /C double_clicked_batch_file_name. In a command window, %cmdcmdline% just has cmd.exe in it (no path). I use CALL mybat rather than cmd.exe /C mybat, but you may have to work with existing batch files.

like image 8
John D Avatar answered Nov 18 '22 11:11

John D