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.
Use:
cmd /K myBatch.bat
as your shortcut.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With