Windows batch file using start not placing multiple programs in background - windows

I am trying to get data from a sql server and ldap server for multiple clients. I need to get the sql data first and then the ldap data. In Unix shell it was straight forward to make a loop around a sub process with both retrievals going on for each client and then wait for it to complete. As a windows batch file however it happens sequentially. I.e. until I retrieve the data for one client, it won't go to the next. How can I get each client's data simultaneously? Here's what I have:
REM Get DB and client info. from config file
for /F "tokens=1,2,3,4,5,6 delims=| eol=#" %%G in (%cfg%\%env%.data) do (
REM Mark file as being in process of receiving data
type nul > %%K.tmp
REM Get data and remove tmp file to indicate completion
start cmd /C sqlcmd .... -Q "A long query" ^> %%K.dat1 && "c:\Program Files\Softerra\LDAP Administrator 4\laimex.exe" ... /sql "Another query" > %%K.dat2 && del %%K.tmp
)
For some reason, I need to do the first redirect escaped as ^> while the later one doesn't need that. At this point I am assuming that everything will be retrieved in the background and that I will need to check afterwards for when the processes are complete which I would do by checking the existence of the zero byte temp files I create. What happens though is that each iteration through the loop only starts when the prior one completes rather than occurring straight away by being placed in the background. Can anyone suggest how I can fix this?

You need to escape the && as well (^&^&), otherwise it executes everything after it as soon as start is fired. Example:
1 gets executed in a new shell correctly, while 2 takes over the main window (not what you want).
start cmd /C ping 127.0.0.1 && ping 127.0.0.2
Both get executed one after the other in a new window.
start cmd /C ping 127.0.0.1 ^&^& ping 127.0.0.2
Same as above, another way to do it.
start cmd /C "ping 127.0.0.1 && ping 127.0.0.2"
Also escape the other >'s, this might work:
start cmd /C sqlcmd .... -Q "A long query" ^> %%K.dat1 ^&^& "c:\Program Files\Softerra\LDAP Administrator 4\laimex.exe" ... /sql "Another query" ^> %%K.dat2 ^&^& del %%K.tmp

Related

set batch script for loop to NOT wait for previous iteration to complete

I have the following script which launches multiple sessions of RDP connection files at once (colleted from a folder which only contains rdp files).
If I launch this from cmd prompt, it launches all sessions in parallel (which is what I want)
for /r %i in (*.rdp) do (mstsc %~nxi /f)
**while **if i run this script, it just launches the first session then waits for the relative process to end before running the second connection and so on.
for /r %%i in (*.rdp) do (mstsc %%~nxi /f)
What I'm doing wrong? Shouldn't it be the default behavior of batch to run all commands in parallel?
I've checked this but it doesn't address my exact scenario and it doesn't work anyhow as expected (e.g. START myBatchScript.bat doens't change the "waiting for process" behavior)
EDIT
Added answer based on comments (Thanks to #Compo and #Modi)
The coding solution to replicate the cmd behavior as a batch script is using the "start" command (as below, thanks to #Compo)
#for /r %i in (*.rdp) do #(start "" mstsc "%~nxi" /f)
for the reason why this happens refer to #Mofi comments.

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 run batch files in background while running multiple files in parallel

I am writing a batch file which will execute 4 other batch files in parallel:
#echo off
echo %time%
(
start call s1.bat
start call s2.bat
start call s3.bat
start call s4.bat
) | set /P "="
echo %time%
But this is opening 4 new windows.
Can anyone help, how to avoid opening multiple windows and run all those 4 batch files in background?
Adding /B is on of the option, but I don't to where to add it exactly.
(
start /B call s1.bat
start /B call s2.bat
start /B call s3.bat
start /B call s4.bat
) | set /P "="
Is giving me an error:
The process tried to write to a nonexistent pipe.
As I do not want to use VB or any other script, how to do it in batch?
This is a really interesting problem! It seems that there is some conflict with the redirection handles.
The main script awaits execution of the sub-scripts with the following code (on Windows 10):
(
> nul start "" /B cmd /D /C call s1.bat
> nul start "" /B cmd /D /C call s2.bat
> nul start "" /B cmd /D /C call s3.bat
> nul start "" /B cmd /D /C call s4.bat
) | set /P =""
The > nul redirection prevents any data to be written to the pipe |. It seems as soon as there becomes anything written to the standard output handle STDOUT (1), the main script does no longer wait (and sometimes the error message The process tried to write to a nonexistent pipe. arises, possibly depending on how fast the sub-script(s) execute(s)). However, as soon as the /B is removed, output to STDOUT by the sub-scripts does not affect the behaviour of the main script, which waits for all sub-scripts, and > nul is therefore no longer necessary.
The explicit cmd /C part is necessary because start would otherwise implicitly use cmd /K when starting internal commands like call which leave non-terminated processes cmd.exe behind upon completion of the respective sub-script.
The /D switch just prevents any auto-run commands to become executed.
When you use normal start without /B for running the sub-scripts, a new instance of the command interpreter is started, that is the process cmd.exe, and a new instance of the console window is started as a process conhost.exe.
Now using start /B still creates a new process cmd.exe but it uses the current process conhost.exe.
I do not know what exactly happens in the background and how all these processes interact, but it appears to me that the issue is connected to the lack of conhost.exe processes when using start /B. Maybe conhost.exe is establishing the redirection handles and hence the pipe. Anyway, as long as there is nothing written to the pipe, there is nothing to conflict, so set /P ="" waits for the related handle to become closed.

A way to Pass a Variable to a Networked path Batch file and Execute

Going to lay this out the best I can and see if someone here can help me out a bit.
Here is my Code .bat on the Remote Server.
echo off
title SystemPlatzAll
set /p input=
findstr %input% SysPlatzAll.log >> Result.txt
%SystemRoot%\explorer.exe "Result.txt"
pause
What im trying to achieve is sending the %input% across the network to this batch file then excute and in return have the file save to the computer it is on. To which then the user will get the file opened from a shared folder i have on the drive.
can I use PsExec to send this over or is there another way?
I can get the .bat to execute with the following.
psexec \\HIFRP010.ad.foo.com -u hoem\hoemfooprod -p !foounit123 -e -h -accepteula -i 0 -d F:\Public\Logfiles\Systemplatz\foo\SystemPlatzBackup1.1\Final\NextTest.bat
pause
The above code will execute the Program.
But I want to know how or if it is possible to instead send %input% from one .bat to another.
Thank you in advance.
It is possible. I am not familiar with psexec, but you may be able to encapsulate the last parameter in doublequotes as is common on windows and pass input params directly on the command line. I do this using runas fairly often.
"F:\Public\Logfiles\Systemplatz\foo\SystemPlatzBackup1.1\Final\NextTest.bat paramFoo paramBar"

Read variable from external file not working on running as scheduled task

I would like to run a batch file after resuming from sleep state in Windows.
If I start the batch file on command line everything works as expected.
But the batch script does not run properly as scheduled task.
What I have done:
External config file AutoMountConf.bat contains set Pass = Test
Local script file scheduleTask.bat contains
rem AutoMountConf.bat is in my intranet.
call X:\AutoMountConf.bat
start "" "C:\Program Files\TrueCrypt\TrueCrypt.exe" /auto favorites /p %Pass% /q
On command line the TrueCrypt container is mounted.
If I run the script from scheduled task I get the login screen to type the password manually.
There are two or perhaps even three issues.
The first one is set Pass = Test instead of set "Pass=Test" as Stephan reported already. For more details on how to assign a value right to an environment variable see my answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line?
The second issue is caused by the fact that network drives once mapped by a user to a drive letter and remembered in registry by Windows are automatically disconnected by Windows on user logs off and are only reconnected if the same user logs on again.
For a scheduled task it is therefore very often necessary to use UNC paths for files and folders on a shared folder in network or connect the network drive and disconnect it in the batch file itself executed as scheduled task.
It is not possible to call a batch file with UNC path. Windows does not allow that. Therefore it is necessary to connect and disconnect to network share manually in the batch file. I offer 2 solutions for this problem.
The first one is using command net use:
%SystemRoot%\System32\net.exe use X: \\ComputerName\ShareName password /user:Domain\UserName /persistent:no
if not errorlevel 1 (
call X:\AutoMountConf.bat
%SystemRoot%\System32\net.exe use X: /delete
start "" /wait "C:\Program Files\TrueCrypt\TrueCrypt.exe" /auto favorites /p %Pass% /q
)
password and /user:Domain\UserName is necessary only if the scheduled task is not executed with a user account which has the permissions to access the batch file on the remote machine. In general it is much more secure to define the scheduled task with the right user account and safe the password also for this account together with the task. Windows stores the password for the task encrypted like it does it also for the user account itself.
Run in a command prompt windows net use /? for details on the required and optional options. /persistent:no is what avoids remembering the network share in Windows registry for automatic reconnect after login by same user.
The second one is using commands pushd and popd:
pushd \\ComputerName\ShareName
if not errorlevel 1 (
call AutoMountConf.bat
popd
start "" /wait "C:\Program Files\TrueCrypt\TrueCrypt.exe" /auto favorites /p %Pass% /q
)
Please execute in a command prompt window pushd /? and read the output help to understand why this works.
But this solution requires that the user account used for the scheduled task with correct password is one which has appropriate permissions on the share on the remote computer. Password and user name can't be specified with this solution in the batch file itself.
if not errorlevel 1 means if previous command exited NOT with a value greater or equal 1 meaning if exit code of previous command is 0 and therefore command execution was successful. It can always happen that the remote machine is currently not available on network and therefore it is always good to check success on connecting to share on remote machine.
There is perhaps one more reason why Pass is not defined after running AutoMountConf.bat.
AutoMountConf.bat contains setlocal and the variable Pass is defined after this command was executed and before endlocal is executed in same batch file or implicitly called by command processor on exiting AutoMountConf.bat.
setlocal results in creating always a copy of existing environment variables and all modifications on environment variables are done on this local copy. The previous environment variables are restored on execution of (matching) endlocal or when end of a batch file is reached in which case the command processor automatically restores previous environment.
Please execute in a command prompt window setlocal /? and read the output help.
For examples to understand environment management by commands setlocal and endlocal perhaps even better see answers on Echoing a URL in Batch and Why is my cd %myVar% being ignored?
set Pass = Test
sets a variable pass<space> with the Content <space>Test. So %pass% keeps empty.
use this Syntax:
set "Pass=Test"
to avoid any unintended spaces.

Resources