How to close the parent cmd console window from within the script it's hosting? - windows

I am using the following command in the registry to run a vbscript locally from Windows Explorer's context menu:
cmd /T:1F /K "cscript.exe //nologo "C:\Some Path\Some Folder\MyScript.vbs" "%1""
I am using the /K switch for 2 reasons: to keep the console window open in case of script errors, and to change the back and fore colors of the console (this is actually done with the /T switch, but for some reason that I don't understand, the colors revert to default when the /C switch is used.
My question: how can I close the host console window (attached to the "cmd.exe" process) on normal execution end? WScript.Quit obviously returns to the console prompt. I thought about a WMI query for Win32_Process based on CommandLine, but that doesn't work if the script is invoked multiple times with the same parameter (i.e. file). Is there a way to obtain the top most parent process's id for example, which I can then use to terminate?

I believe you can simply append &&exit to your command string:
cmd /T:1F /K "cscript.exe //nologo "C:\Some Path\Some Folder\MyScript.vbs" "%1"&&exit"
Note that your quoted arguments are not quoted because of the extra enclosing quotes. You might be better off with:
cmd /T:1F /K ^"cscript.exe //nologo "C:\Some Path\Some Folder\MyScript.vbs" "%~1"^&&exit^"
Update
The script engine does not set the exit code upon runtime errors - it leaves that task to the script writer. It does set a positive exit code if there is a syntax error. That is a very unfortunate design.
I think the easiest thing to do is to modify your VBS script to always exit with a negative value upon success: WScript.Quit -1. Then you can conditionally EXIT the console only if the exit code is negative.
cmd /T:1F /K ^"cscript.exe //nologo "C:\Some Path\Some Folder\MyScript.vbs" "%~1"^||if not errorlevel 0 exit^"

Related

Running Flutter/Dart commands in batch script halts execution [duplicate]

I'm trying to get my commit-build.bat to execute other .BAT files as part of our build process.
Content of commit-build.bat:
"msbuild.bat"
"unit-tests.bat"
"deploy.bat"
This seems simple enough, but commit-build.bat only executes the first item in the list (msbuild.bat).
I have run each of the files separately with no problems.
Use:
call msbuild.bat
call unit-tests.bat
call deploy.bat
When not using CALL, the current batch file stops and the called batch file starts executing. It's a peculiar behavior dating back to the early MS-DOS days.
All the other answers are correct: use call. For example:
call "msbuild.bat"
History
In ancient DOS versions it was not possible to recursively execute batch files. Then the call command was introduced that called another cmd shell to execute the batch file and returned execution back to the calling cmd shell when finished.
Obviously in later versions no other cmd shell was necessary anymore.
In the early days many batch files depended on the fact that calling a batch file would not return to the calling batch file. Changing that behaviour without additional syntax would have broken many systems like batch menu systems (using batch files for menu structures).
As in many cases with Microsoft, backward compatibility therefore is the reason for this behaviour.
Tips
If your batch files have spaces in their names, use quotes around the name:
call "unit tests.bat"
By the way: if you do not have all the names of the batch files, you could also use for to do this (it does not guarantee the correct order of batch file calls; it follows the order of the file system):
FOR %x IN (*.bat) DO call "%x"
You can also react on errorlevels after a call. Use:
exit /B 1 # Or any other integer value in 0..255
to give back an errorlevel. 0 denotes correct execution. In the calling batch file you can react using
if errorlevel neq 0 <batch command>
Use if errorlevel 1 if you have an older Windows than NT4/2000/XP to catch all errorlevels 1 and greater.
To control the flow of a batch file, there is goto :-(
if errorlevel 2 goto label2
if errorlevel 1 goto label1
...
:label1
...
:label2
...
As others pointed out: have a look at build systems to replace batch files.
If we want to open multiple command prompts then we could use
start cmd /k
/k: is compulsory which will execute.
Launching many command prompts can be done as below.
start cmd /k Call rc_hub.bat 4444
start cmd /k Call rc_grid1.bat 5555
start cmd /k Call rc_grid1.bat 6666
start cmd /k Call rc_grid1.bat 5570.
Try:
call msbuild.bat
call unit-tests.bat
call deploy.bat
You are calling multiple batches in an effort to compile a program.
I take for granted that if an error occurs:
1) The program within the batch will exit with an errorlevel;
2) You want to know about it.
for %%b in ("msbuild.bat" "unit-tests.bat" "deploy.bat") do call %%b|| exit /b 1
'||' tests for an errorlevel higher than 0. This way all batches are called in order but will stop at any error, leaving the screen as it is for you to see any error message.
If we have two batch scripts, aaa.bat and bbb.bat, and call like below
call aaa.bat
call bbb.bat
When executing the script, it will call aaa.bat first, wait for the thread of aaa.bat terminate, and call bbb.bat.
But if you don't want to wait for aaa.bat to terminate to call bbb.bat, try to use the START command:
START ["title"] [/D path] [/I] [/MIN] [/MAX] [/SEPARATE | /SHARED]
[/LOW | /NORMAL | /HIGH | /REALTIME | /ABOVENORMAL | /BELOWNORMAL]
[/AFFINITY <hex affinity>] [/WAIT] [/B] [command/program]
[parameters]
Exam:
start /b aaa.bat
start /b bbb.bat
call msbuild.bat
call unit-tests.bat
call deploy.bat
using "&"
As you have noticed executing the bat directly without CALL,START, CMD /C causes to enter and execute the first file and then the process to stop as the first file is finished. Though you still can use & which will be the same as using command1 & command2 directly in the console:
(
first.bat
)&(
second.bat
)& (
third.bat
)&(
echo other commands
)
In a term of machine resources this will be the most efficient way though in the last block you won't be able to use command line GOTO,SHIFT,SETLOCAL.. and its capabilities will almost the same as in executing commands in the command prompt. And you won't be able to execute other command after the last closing bracket
Using CALL
call first.bat
call second.bat
call third.bat
In most of the cases it will be best approach - it does not create a separate process but has almost identical behaviour as calling a :label as subroutine. In MS terminology it creates a new "batch file context and pass control to the statement after the specified label. The first time the end of the batch file is encountered (that is, after jumping to the label), control returns to the statement after the call statement."
You can use variables set in the called files (if they are not set in a SETLOCAL block), you can access directly labels in the called file.
CMD /C, Pipes ,FOR /F
Other native option is to use CMD /C (the /C switch will force the called console to exit and return the control)
Something that cmd.exe is doing in non transparent way with using FOR /F against bat file or when pipes are used.
This will spawn a child process that will have all the environment ot the calling bat.
Less efficient in terms of resources but as the process is separate ,parsing crashes or calling an EXIT command will not stop the calling .bat
#echo off
CMD /c first.bat
CMD /C second.bat
::not so different than the above lines.
:: MORE,FINDSTR,FIND command will be able to read the piped data
:: passed from the left side
break|third.bat
START
Allows you more flexibility as the capability to start the scripts in separate window , to not wait them to finish, setting a title and so on. By default it starts the .bat and .cmd scripts with CMD /K which means that the spawned scripts will not close automatically.Again passes all the environment to the started scripts and consumes more resources than cmd /c:
:: will be executed in the same console window and will wait to finish
start "" /b /w cmd /c first.bat
::will start in a separate console window and WONT wait to be finished
:: the second console window wont close automatically so second.bat might need explicit exit command
start "" second.bat
::Will start it in a separate window ,but will wait to finish
:: closing the second window will cause Y/N prompt
:: in the original window
start "" /w third.cmd
::will start it in the same console window
:: but wont wait to finish. May lead to a little bit confusing output
start "" /b cmd /c fourth.bat
WMIC
Unlike the other methods from now on the examples will use external of the CMD.exe utilities (still available on Windows by default).
WMIC utility will create completely separate process so you wont be able directly to wait to finish. Though the best feature of WMIC is that it returns the id of the spawned process:
:: will create a separate process with cmd.exe /c
WMIC process call create "%cd%\first.bat","%cd%"
::you can get the PID and monitoring it with other tools
for /f "tokens=2 delims=;= " %%# in ('WMIC process call create "%cd%\second.bat"^,"%cd%" ^|find "ProcessId"') do (
set "PID=%%#"
)
echo %PID%
You can also use it to start a process on a remote machine , with different user and so on.
SCHTASKS
Using SCHTASKS provides some features as (obvious) scheduling , running as another user (even the system user) , remote machine start and so on. Again starts it in completely separate environment (i.e. its own variables) and even a hidden process, xml file with command parameters and so on :
SCHTASKS /create /tn BatRunner /tr "%cd%\first.bat" /sc ONCE /sd 01/01/1910 /st 00:00
SCHTASKS /Run /TN BatRunner
SCHTASKS /Delete /TN BatRunner /F
Here the PID also can acquired from the event log.
ScriptRunner
Offers some timeout between started scripts. Basic transaction capabilities (i.e. rollback on error) and the parameters can be put in a separate XML file.
::if the script is not finished after 15 seconds (i.e. ends with pause) it will be killed
ScriptRunner.exe -appvscript %cd%\first.bat -appvscriptrunnerparameters -wait -timeout=15
::will wait or the first called script before to start the second
:: if any of the scripts exit with errorcode different than 0 will try
:: try to restore the system in the original state
ScriptRunner.exe -appvscript second.cmd arg1 arg2 -appvscriptrunnerparameters -wait -rollbackonerror -appvscript third.bat -appvscriptrunnerparameters -wait -timeout=30 -rollbackonerror
To call a .bat file within a .bat file, use
call foo.bat
(Yes, this is silly, it would make more sense if you could call it with foo.bat, like you could from the command prompt, but the correct way is to use call.)
Simplest Way To Run Multiple Batch Files Parallelly
start "systemLogCollector" /min cmd /k call systemLogCollector.bat
start "uiLogCollector" /min cmd /k call uiLogCollector.bat
start "appLogCollector" /min cmd /k call appLogCollector.bat
Here three batch files are run on separate command windows in a minimized state. If you don't want them minimized, then remove /min. Also, if you don't need to control them later, then you can get rid of the titles. So, a bare-bone command will be- start cmd /k call systemLogCollector.bat
If you want to terminate them, then run these commands-
taskkill /FI "WindowTitle eq appLogCollector*" /T /F
taskkill /FI "WindowTitle eq uiLogCollector*" /T /F
taskkill /FI "WindowTitle eq systemLogCollector*" /T /F
Start msbuild.bat
Start unit-tests.bat
Start deploy.bat
If that doesn't work, replace start with call or try this:
Start msbuild.bat
Goto :1
:1
Start unit-tests.bat
Goto :2
:2
Start deploy.bat
Looking at your filenames, have you considered using a build tool like NAnt or Ant (the Java version). You'll get a lot more control than with bat files.
If you want to open many batch files at once you can use the call command. However, the call command closes the current bat file and goes to another. If you want to open many at once, you may want to try this:
#echo off
start cmd "call ex1.bat&ex2.bat&ex3.bat"
And so on or repeat start cmd "call..." for however many files. This works for Windows 7, but I am not sure about other systems.
Your script should be:
start "msbuild.bat"
start "unit-tests.bat"
start "deploy.bat"
Just use the call command! Here is an example:
call msbuild.bat
call unit-tests.bat
call deploy.bat
With correct quoting (this can be tricky sometimes):
start "" /D "C:\Program Files\ProgramToLaunch" "cmd.exe" "/c call ""C:\Program Files\ProgramToLaunch\programname.bat"""
1st arg - Title (empty in this case)
2nd arg - /D specifies starting directory, can be ommited if want the current working dir (such as "%~dp0")
3rd arg - command to launch, "cmd.exe"
4th arg - arguments to command, with doubled up quotes for the arguments inside it (this is how you escape quotes within quotes in batch)
Running multiple scripts in one I had the same issue. I kept having it die on the first one not realizing that it was exiting on the first script.
:: OneScriptToRunThemAll.bat
CALL ScriptA.bat
CALL ScriptB.bat
EXIT
:: ScriptA.bat
Do Foo
EXIT
::ScriptB.bat
Do bar
EXIT
I removed all 11 of my scripts EXIT lines and tried again and all 11 ran in order one at a time in the same command window.
:: OneScriptToRunThemAll.bat
CALL ScriptA.bat
CALL ScriptB.bat
EXIT
::ScriptA.bat
Do Foo
::ScriptB.bat
Do bar
I know I am a bit late to the party, but here is another way. That is, this method should wait until the first one is done, the second, and so on.
start "" /wait cmd.exe /c msbuild.bat
start "" /wait cmd.exe /c unit-tests.bat
start "" /wait cmd.exe /c deploy.bat
The only issue that may come out of using this method, is that with new instances of cmd.exe being spawned, is that Errorlevel checking is kept within in each instance of cmd.exe.
Or..
start "" /wait call msbuild.bat
start "" /wait call unit-tests.bat
start "" /wait call deploy.bat
Hope this helps.

How to open new cmd window and execute for-loop in it?

I have a test.cmd file with the following command:
call "cmd /c start echo foo && pause"
call "cmd /c start for /l %%x in (0, 1, 2) do python test.py config%%x"
The first command is working fine and shows that the general approach should work. The second one with the for loop gives me troubles.
When I run this command directly in a CMD window (with only one % sign before the iterator), it starts my python script "test.py" in a new CMD window 3 times in a loop as expected.
When I run the same command from my test.cmd (this time with two % of course), the new CMD window pops up and is gone right away. I don't get any error messages and can't get the new window to stay.
I suspect that I need to do some more encoding but I cannot figure out the correct syntax. What must I change to get this for loop to run from my test.cmd?
if you insist on using call, there is another level of parsing, so in a batchfile you have to use:
call "cmd /k start for /l %%%%i in (1,1,10) do echo %%%%i"
(replaced /c with /k to see the output)
try this:
call "cmd /c for /l %x in (0, 1, 2) do #python test.py config%x"
when you execute loops in command prompt you need single percentage symbol.
And you need to remove the start command after the cmd /c because for is internal cmd command. The # is for suppress printing of prompt value on each iteration.
This answer elaborates on the core question stated in the title of your post rather than on the sample code you provided.
To run a for loop in a separate (cmd) window, the start command is needed. In addition, An explicit cmd instance should be given in order to avoid problems with start when calling internal command lines (for instance, parentheses may cause errors), and also to have control over what happens with the new window after the loop has finished.
The following command line runs a for loop in a new cmd window, which remains open after the loop finished (quite helpful for debugging):
start "" /WAIT cmd /K "for /L %%X in (0,1,2) do echo %%X"
Type exit into the new window to close it. To automatically close the new window finally, replace /K by /C.
If you do not want your main (calling) batch script to wait for the loop to finish and the new window to be closed, remove the /WAIT switch. This way the command line is started, but the main script immediately continues executing. This allows you to run multiple for loops in separate windows simultaneously.
This technique is not limited to calling for loops only.

How to start a batch file minimized with task scheduler in Windows 8? - %comspec% method not working anymore after Windows 7

After Windows XP, I always use the trick below to start batch files minimized with Windows Task Manager.
From http://www.pcreview.co.uk/forums/running-bat-files-minimized-scheduler-t2125918.html:
"prequisite: all your batch files have an exit-command to finish the actions off. If you do not exit, you will end with a command prompt blinking.
This is what I keep using:
%comspec% /c start /min "C:\Scripts\Destination_inbound_ftp5.bat"
When you save this in the properties, you will get a follow-up dialogue asking you if you meant all this to be parameters or not. Answer NO and the task will be saved as you would expect.
I also read the Stack Overflow question “start %comspec% /c script.cmd” vs “start cmd /C second.cmd script.cmd”, which made me replace the "%comspec%" statement with "C:\Windows\system32\cmd.exe", but that did not change anything either.
The problem is that now, instead of a minimized running bat file, I end up with just a command prompt, minimized but without any of the batch commands executed. The task scheduler status remains "running" :(
How do I get this done on Windows 8 (64-bit)? Preferrable with old-school batch commands instead of PowerShell (or worse ;p)
The start command needs the leading "" quotes to disable the title feature. Try scheduling this:
%comspec% /c start "" /min "C:\Scripts\Destination_inbound_ftp5.bat" ^& exit
Assuming Windows 8 is the same as Windows 7, an "exit" is only going to exit the batch file (which it is going to do anyway).
You need to add the exit code like this:
Under "Program/Script":
CMD (or command.exe, or %comspec%)
Under "Arguments:
/c start "Title" /min "C:\Scripts\Destination_inbound_ftp5.bat" ^& exit
I didn't like seeing the command window pop up and then disappear so here is another solution from https://ss64.com/vb/run.html ...
First create invisible.vbs with this single line of text:
CreateObject("Wscript.Shell").Run """" & WScript.Arguments(0) & """", 0, False
Next and finally, launch your cmd or batch file via:
%SystemRoot%\system32\wscript.exe "invisible.vbs" "myscript.cmd" //nologo
Ta da! Scripting of this sort has been built into Windows for a long time. If you're curious, do a web search for "WSH" (windows scripting host). You can even write such scripts in dialect of JavaScript called JScript.
Another possibility: a small freeware program named CMDH, that simply runs the requested orders in background.
For example:
cmdh MyScript.cmd
No need to add "exit" to the script.
Tested working in Windows XP SP3, and there is no reason it should fail on Windows 8.
Here's a solution from https://ss64.com/vb/run.html that will run a batch file in a minimized window. Unlike the other solutions that use the start command with /min, this one will not flash a new window onto your screen or interrupt full-screen activities. It does, however, steal focus. I don't know how to avoid that.
First create a file named run_minimized.vbs with this single line of text:
CreateObject("Wscript.Shell").Run """" & WScript.Arguments(0) & """", 2, False
Next, create your Task Scheduler task with an action to start the program wscript.exe with these arguments:
"c:\path\run_minimized.vbs" "c:\path\my script.bat"
Change the paths as necessary to specify the locations of the two files.
There is no simple way to pass arguments from Task Scheduler to the batch file while also preserving spaces and quotation marks, because wscript strips quotation marks from its arguments. The simplest way to handle arguments with spaces would be to put the entire batch file command into the vbs:
CreateObject("Wscript.Shell").Run """c:\path\my script.bat"" ""arg 1"" arg2", 2, False
Note the use of quotation marks. There's one pair of quotation marks " enclosing the entire command string, and a pair of adjacent quote characters "" every place you'd use a normal quotation mark in a command line.
Maybe it's not the same as you mean but if I simply run a .bat file with the scheduler and tick this "Hidden" box on the General tab of the task properties it starts minified.
As already mentioned in foxidrive's answer, the issue is caused by the missing title parameter, which is taken from the first quoted argument string on the command line of the start command, which is the quoted batch file path in your command line. Since there is nothing left to be taken as the command/program to be started, the default cmd.exe is started.
Providing a dummy title (which may even be empty) solves that issue:
%ComSpec% /C start "" /MIN "C:\Scripts\Destination_inbound_ftp5.bat"
However, as mentioned in the help message of start (type start /? into a command prompt window), when the provided command/program is a batch file, the command processor cmd.exe is run with the /K switch, which keeps the command prompt window open:
command/program
If it is an internal cmd command or a batch file then
the command processor is run with the /K switch to cmd.exe.
This means that the window will remain after the command
has been run.
If it is not an internal cmd command or batch file then
it is a program and will run as either a windowed application
or a console application.
To prevent that, let us explicitly specify the command processor with the /C switch:
%ComSpec% /C start "" /MIN %ComSpec% /C "C:\Scripts\Destination_inbound_ftp5.bat"
So there is no additional exit command necessary.

How do I minimize the command prompt from my bat file

I have this bat file and I want to minimize the cmd window when I run it:
#echo off
cd /d C:\leads\ssh
call C:\Ruby192\bin\setrbvars.bat
ruby C:\leads\ssh\put_leads.rb
I want the command window minimized immediately. Any ideas on how to do this?
There is a quite interesting way to execute script minimized by making him restart itself minimised. Here is the code to put in the beginning of your script:
if not DEFINED IS_MINIMIZED set IS_MINIMIZED=1 && start "" /min "%~dpnx0" %* && exit
... script logic here ...
exit
How it works
When the script is being executed IS_MINIMIZED is not defined (if not DEFINED IS_MINIMIZED) so:
IS_MINIMIZED is set to 1: set IS_MINIMIZED=1.
Script starts a copy of itself using start command && start "" /min "%~dpnx0" %* where:
"" - empty title for the window.
/min - switch to run minimized.
"%~dpnx0" - full path to your script.
%* - passing through all your script's parameters.
Then initial script finishes its work: && exit.
For the started copy of the script variable IS_MINIMIZED is set by the original script so it just skips the execution of the first line and goes directly to the script logic.
Remarks
You have to reserve some variable name to use it as a flag.
The script should be ended with exit, otherwise the cmd window wouldn't be closed after the script execution.
If your script doesn't accept arguments you could use argument as a flag instead of variable:
if "%1" == "" start "" /min "%~dpnx0" MY_FLAG && exit
or shorter
if "%1" == "" start "" /min "%~f0" MY_FLAG && exit
Use the start command, with the /min switch to run minimized. For example:
start /min C:\Ruby192\bin\setrbvars.bat
Since you've specified a batch file as the argument, the command processor is run, passing the /k switch. This means that the window will remain on screen after the command has finished. You can alter that behavior by explicitly running cmd.exe yourself and passing the appropriate switches if necessary.
Alternatively, you can create a shortcut to the batch file (are PIF files still around), and then alter its properties so that it starts minimized.
The only way I know is by creating a Windows shortcut to the batch file and then changing its properties to run minimized by default.
Using PowerShell you can minimize from the same file without opening a new instance.
powershell -window minimized -command ""
Also -window hidden and -window normal is available to hide completely or restore.
source: https://stackoverflow.com/a/45061676/1178975
If you want to start the batch for Win-Run / autostart, I found I nice solution here https://www.computerhope.com/issues/ch000932.htm & https://superuser.com/questions/364799/how-to-run-the-command-prompt-minimized
cmd.exe /c start /min myfile.bat ^& exit
the cmd.exe is needed as start is no windows command that can be executed outside a batch
/c = exit after the start is finished
the ^& exit part ensures that the window closes even if the batch does not end with exit
However, the initial cmd is still not minimized.
One way to 'minimise' the cmd window is to reduce the size of the console using something like...
echo DO NOT CLOSE THIS WINDOW
MODE CON COLS=30 LINES=2
You can reduce the COLS to about 18 and the LINES to 1 if you wish.
The advantage is that it works under WinPE, 32-bit or 64-bit, and does not require any 3rd party utility.
If you type this text in your bat file:
start /min blah.exe
It will immediately minimize as soon as it opens the program. You will only see a brief flash of it and it will disappear.
You could try running a script as follows
var WindowStyle_Hidden = 0
var objShell = WScript.CreateObject("WScript.Shell")
var result = objShell.Run("cmd.exe /c setrbvars.bat", WindowStyle_Hidden)
save the file as filename.js
Yet another free 3rd party tool that is capable of minimizing the console window at any time (not only when starting the script) is Tcl with the TWAPI extension:
echo package require twapi;twapi::minimize_window [twapi::get_console_window] | tclkitsh -
here tclkitsh.exe is in the PATH and is one of the tclkit-cli-*-twapi-*.exe files downloadable from sourceforge.net/projects/twapi/files/Tcl binaries/Tclkits with TWAPI/. I prefer it to the much lighter min.exe mentioned in Bernard Chen's answer because I use TWAPI for countless other purposes already.
You can minimize the command prompt on during the run but you'll need two additional scripts: windowMode and getCmdPid.bat:
#echo off
call getCmdPid
call windowMode -pid %errorlevel% -mode minimized
cd /d C:\leads\ssh
call C:\Ruby192\bin\setrbvars.bat
ruby C:\leads\ssh\put_leads.rb
One option is to find one of the various utilities that can change the window state of the currently running console window and make a call to it from within the batch script.
You can run it as the first thing in your batch script. Here are two such tools:
min.exe
http://www.paulsadowski.com/wsh/cmdprogs.htm
cmdow
http://www.commandline.co.uk/cmdow/index.html
Another option that works fine for me is to use ConEmu, see http://conemu.github.io/en/ConEmuArgs.html
"C:\Program Files\ConEmu\ConEmu64.exe" -min -run myfile.bat
try these
CONSOLESTATE /Min
or:
SETCONSOLE /minimize
or:
TITLE MinimizeMePlease
FOR /F %%A IN ('CMDOW ˆ| FIND "MinimizeMePlease"') DO CMDOW %%A /MIN
http://conemu.github.io/en/ConEmuArgs.html download flagged by Virus Total.
May have Malware.

Pausing a batch file when double-clicked but not when run from a console window?

Is there a way for a batch file (in this case, running on Windows XP) to determine whether it was launched from a command line (i.e. inside a console window) or launched via the shell (e.g. by double-clicking)?
I have a script which I'd like to have pause at certain points when run via the shell, but not when run at a command line. I've seen a similar question on SO, but am unable to use the same solution for two reasons: first, whether or not it pauses needs to be dependent on multiple factors, only one of which is whether it was double-clicked. Second, I'll be distributing this script to others on my team and I can't realistically ask all of them to make registry changes which will affect all scripts.
Is this possible?
Found one :-) – After desperately thinking of what cmd might do when run interactively but not when launching a batch file directly ... I finally found one.
The pseudo-variable %cmdcmdline% contains the command line that was used to launch cmd. In case cmd was started normally this contains something akin to the following:
"C:\Windows\System32\cmd.exe"
However, when launching a batch file it looks like this:
cmd /c ""C:\Users\Me\test.cmd" "
Small demo:
#echo off
for %%x in (%cmdcmdline%) do if /i "%%~x"=="/c" set DOUBLECLICKED=1
if defined DOUBLECLICKED pause
This way of checking might not be the most robust, though, but /c should only be present as an argument if a batch file was launched directly.
Tested here on Windows 7 x64. It may or may not work, break, do something weird, eat children (might be a good thing) or bite you in the nose.
A consolidated answer, derived from much of the information found on this page (and some other stack overflow pages with similar questions). This one does not rely on detecting /c, but actually checks for the name of the script in the command line. As a result this solution will not pause if you double-clicked on another batch and then called this one; you had to double-click on this particular batch file.
:pauseIfDoubleClicked
setlocal enabledelayedexpansion
set testl=%cmdcmdline:"=%
set testr=!testl:%~nx0=!
if not "%testl%" == "%testr%" pause
The variable "testl" gets the full line of the cmd processor call, stripping out all of the pesky double quotes.
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).
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 (or called from another batch file), go on.
Edit: The same can be done in a single line:
echo %cmdcmdline% | findstr /i /c:"%~nx0" && set standalone=1
In plain English, this
pipes the value of %cmdcmdline% to findstr, which then searches for the current script name
%0 contains the current script name, of course only if shift has not been called beforehand
%~nx0 extracts file name and extension from %0
>NUL 2>&1 mutes findstr by redirecting any output to NUL
findstr sets a non-zero errorlevel if it can't find the substring in question
&& only executes if the preceding command returned without error
as a consequence, standalone will not be defined if the script was started from the command line
Later in the script we can do:
if defined standalone pause
One approach might be to create an autoexec.nt file in the root of c:\ that looks something like:
#set nested=%nested%Z
In your batch file, check if %nested% is "Z" - if it is "Z" then you've been double-clicked, so pause. If it's not "Z" - its going to be "ZZ" or "ZZZ" etc as CMD inherits the environment block of the parent process.
-Oisin
A little more information...
I start with a batch-file (test.cmd) that contains:
#echo %cmdcmdline%
If I double-click the "test.cmd" batch-file from within Windows Explorer, the display of echo %cmdcmdline% is:
cmd /c ""D:\Path\test.cmd" "
When executing the "test.cmd" batch-file from within a Command Prompt window, the display of
echo %cmdcmdline% depends on how the command window was started...
If I start "cmd.exe" by clicking the "Start-Orb" and "Command Prompt" or if I click "Start-Orb" and execute "cmd.exe" from the search/run box. Then I execute the "test.cmd" batch-file, the display of echo %cmdcmdline% is:
"C:\Windows\system32\cmd.exe"
Also, for me, if I click "Command Prompt" from the desktop shortcut, then execute the "test.cmd" batch-file, the display of echo %cmdcmdline% is also:
"C:\Windows\system32\cmd.exe"
But, if I "Right-Click" inside a Windows Explorer window and select "Open Command Prompt Here", then execute the "test.cmd" batch-file, the display of echo %cmdcmdline% is:
"C:\Windows\System32\cmd.exe" /k ver
So, just be careful, if you start "cmd.exe" from a shortcut that contains a "/c" in the "Target" field (unlikely), then the test in the previous example will fail to test this case properly.

Resources