How to call internal functions in Windows batch simultaneously? - windows

I have a windows batch that reads my config files (also batch files) from within a directory. Now I want to call an internal function to process each of this config file. Since it is a for-loop, the files are processed one after another. But I want to execute each function for each file at the same time.
So far, I only found solutions for running commands simultaneously, i.e. the "start" command. But no solution for internal functions so far.
Background: Each config file contains lots of variables with different values that the function can automatically use, without passing them on as arguments.
Each config file contains parameters in order to execute sqlcmd with those and to process and define the output file as well. Since the variables are just too many, I don't want to handover them as arguments and therefore don't want to outsource the function into a seperate .bat file.
For reference, the code excerpt:
FOR %%I IN ("%path_job_sheets%\*.bat") DO (CALL "%%I" & CALL :get_period "%%I")
:get_period
CALL LOTS OF VARIABLES
CALL :get_job
:get_job
DO MORE PARAMETER CALLING
The ":get_period" needs to be executed but the batch should move on calling the next .bat-file.
Has anyone a solution, please?

You could use start.
Change your file a bit, so it starts the current batch file itself (thats %~f0)
#echo off
if "%~1"=="thread_job" goto :thread_job
...
FOR %%I IN ("%path_job_sheets%\*.bat") DO (
CALL "%%I"
CALL :get_period "%%I"
)
exit /b
...
:get_period
set "fileName=%~1"
start "Title %filename%" cmd /c "%~f0" thread_job
exit /b
:thread_job
CALL LOTS OF VARIABLES
CALL :get_job
If you have many jobs then it could be a good idea to use start /b to run all jobs in the same window.

Related

How to find the name/path of a parent batch file from within another batch file RUN, but not CALLed, by the parent?

my goal is to have a general use batch file that is able to be run by name from within another batch file, which will then do meta/introspective things with the parent batch file that ran it. In order to do this, I'm hoping I can identify the parent from within my general use file. I'm aware I can supply the name/path of the parent alongside the command as an argument when the parent runs my file, but to prevent dishonest reporting by the parent file, for aesthetic reasons, and also in the interest of exploring the functionality of batch in general, I would like to avoid doing this and leave all identification to be done by my script, if that is possible.
I've looked into WMIC, and I'm sure I can get the ParentProcessID of my script's cmd.exe instance when it runs, but I haven't found a way to do anything useful with it. I've messed with the (goto) trick, and I know how to get the name/path of the parent if the parent CALLs my script, but since I'm trying to RUN it (i.e. by its bare name as a command), the (goto) trick also hasn't done me any good, since it seems to all be one call stack.
although anyone who messes with batch should already know what I've attempted when I say these things, in the interest of fluffing up this post's content, here's how I'll use the (goto) trick to inspect changes to the call stack:
#ECHO OFF
:: TOPLEVEL.BAT, note I'm not calling my script
"MYSCRIPT.BAT"
#ECHO OFF
:: MYSCRIPT.BAT
SETLOCAL
SET "_SELF_=%~dpnx0"
(
(goto) 2>NUL
ECHO this is the scope I should be in: %~dpnx0
CALL ECHO this is the scope I would break into: %%~dpnx0
(goto) 2>NUL
:: define variable that will be blank while still in nested scope
CALL SET "_COMPLETE_=%%"
IF NOT DEFINED _COMPLETE_ (
:: go one scope deeper and recurse
CALL "%_SELF_%"
) ELSE (
:: back to baseline
CALL "%ComSpec%" /c
)
)
ENDLOCAL
anyway, any wizards out there know whether this is possible or how it could be done?
When chaining batchfiles there is very little info available from previous chains.
There is however the variable %cmdcmdline%.
Depending on the way the first batchfile is started, this variable may contain the name of the first started batchfile.
If manually started from the commandline it has no usefull info, but this can be silently changed to run with cmd/c (see chain0.cmd).
If started with the start-command or started from explorer or started from commandline with cmd/c it will contain the name of the first started batchfile in %cmdcmdline%.
chain0.cmd
#echo off
cls
echo this is: %~0
echo cmdcmdline=[%cmdcmdline%]
if "%comspec%" == %cmdcmdline% cmd /c %0 & exit /b
Chain1.cmd
chain1.cmd
#echo off
echo this is: %~0
echo cmdcmdline=[%cmdcmdline%]
Chain2.cmd
Chain2.cmd
#echo off
echo this is: %~0
echo cmdcmdline=[%cmdcmdline%]
pause

Run Batch Script Across Subfolders (Recursively)

I regularly have to rename hundreds of files across a subfolder structure. I have been creating a batch file consisting of all my rename commands, and manually pasting this into each subfolder to execute one subfolder at a time. I'd like to revise the batch script so that it executes against all subfolders in one fell swoop, run from the parent directory just once.
My renaming is very manual, and so I need to create a discrete entry for each file. For example, here are three lines:
REN STWP01_00669087* BCBSRI-01849351*
REN BCBSRI-01849357* 2011-12-19_BCBSRI-01849357*
REN STWP01_00669094* BCBSRI-01849369*
I've experimented with the FOR /R command, including trying a separate batch file that calls my renaming batch file (via the CALL command). No luck.
I have to assume that this is simple, but I'm a batch novice, so any help would be appreciated.
Thanks!
#Magoo,
Thanks so much for your response. Your approach is going to be far more efficient than my own so far.
A couple of questions. Please bear with me as I am a total novice with batch commands.
Here's what I did: I saved your code to a .BAT file ("RRename.bat"), modified my filenames as per your instructions and saved those to a text file ("Filenames.txt"), and then run this command from the command line: {RRename.bat Filenames.txt}.
The resulting command windows confirm correct renaming. And so I removed the ECHO and PAUSE commands and re-ran. No luck. Just a bunch of Command windows confirming the directory.
Ideally I'd love to save this as a .BAT file and simply drop this in the top-level directory, together with the data file that contains the old names and new names of the files. And so, a double-click of "RRename.bat" will parse the content of "Filenames.txt" and work its way through all subfolders, renaming wherever matches are encountered. Boom.
To that end:
1. How do I make it so {SET "sourcedir=} indicates the current directory (i.e. the directory in which the batch file is located)? This way I wouldn't ever need to change this variable. (I should note that I am running this script on a network location, which requires me to map the drive, resulting in a different drive letter every time.)
2. How do I hard-code the name of the data file into the script itself? My goal is an easily replicated process minimizing user input (save for the content of the data file).
3. How do I stop the individual command windows from appearing? I'll be renaming thousands of files at a time and don't want to see thousands fo corresponding command windows.
Thank you!
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
:: read parameters
SET "filename1=%~1"
SET "filename2=%~2"
IF DEFINED filename2 GOTO name
IF NOT DEFINED filename1 GOTO :EOF
:: 1 parameter - must be filename
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO START /min "ren %%a" "%~f0" %%a
GOTO :eof
:: we have 2 parameters so rename pattern 1 to pattern 2
:name
FOR /r "%sourcedir%" %%a IN ("%filename1%*") DO CALL :process "%%a"
PAUSE
GOTO :EOF
:: Process the filenames and actually do the rename
:process
:: Name of file to be changed - name and extension of %1
SET "changeme=%~nx1"
:: REPLACE up-to-from-pattern with nothing = remainder of name/extension
CALL SET "endpart=%%changeme:*%filename1%=%%"
:: and RENAME...
ECHO(REN "%~1" "%filename2%%endpart%"
GOTO :eof
You would need to change the setting of sourcedir to suit your circumstances.
Revised data file
STWP01_00669087 BCBSRI-01849351
BCBSRI-01849357 2011-12-19_BCBSRI-01849357
STWP01_00669094 BCBSRI-01849369
Aimed at processing the above file, renaming files starting (column1 entries) to start (column2 entries.)
Method:
Run the batch as
batchname filename
This will execute the batch, processing filename
How:
having set the directory name to start processing from, set filename1&2 to the values of the parameters supplied.
If only 1 is supplied, it is the filename, so process it line-by-line and START a new process /min minimised "with the window name in the first set of quotes" and execute this same batch with the data from each line of the file in turn, then finish by going to :eof (end-of-file - built-in to CMD)
The sub-processes all have 2 parameters (eg BCBSRI-01849357 2011-12-19_BCBSRI-01849357) so processing passes to :name. This runs a for /r loop, from the specified source directory, with the name specified from the first column+* and executes :process passing the filenames found as parameter 1.
:process sets changeme to the filename in question, calculates endpart by removing the string filename1 from changeme which will deliver the er, end part.
Then simply rename the supplied filename to the replacement name+that endpart calculated.
The required REN commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(REN to REN to actually rename the files.
The PAUSE is just to allow the proposed changes to be seen. Once the process has been verified, change the PAUSE to EXIT.
AAMOI, running
*batchname* STWP01_00669094 BCBSRI-01849369
for instance, would execute the recursive-rename from STWP01_00669094* to BCBSRI-01849369*
Sadly, "No luck" is meaningless.
I have made a minor, but significant change to the instructions. The PAUSE should be changed to an EXIT after testing.
After testing, the ECHO(... line should become
REN "%~1" "%filename2%%endpart%"
which actually executes the rename. If you've just deleted the line, it would explain the no-visible-result.
Having restored the original code and verified against a small representative dummy subtree, change the echo(... line and test again. The filenames should change. If not, something is dreadfully wrong. Needless to say, this works perfectly happily for me...
Then try again with the PAUSE changed to EXIT. This time, the windows generated will appear on the taskbar and then disappear when the rename for that line of the input file has finished. This will happen once for BCBSRI-01849357 rentwo for instance - not once for each individual file rename occurring.
To hard-code the filename, remove the line
IF NOT DEFINED filename1 GOTO :EOF
and replace
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO START /min "ren %%a" "%~f0" %%a
with
FOR /f "usebackqdelims=" %%a IN ("YOURFILENAMEHERE") DO START /min "ren %%a" "%~f0" %%a
For the "run from here" command, change
SET "sourcedir=U:\sourcedir"
to
SET "sourcedir=."
. means "the current directory"
If you place thisbatchfilename.bat into any directory on your PATH then you can run the routine simply by executing thisbatchfilename.
You can display your path by typing
path
at the prompt. PATH is the sequence of directories searched by windows to find an executable if it isn't found in the current directory. To chane path, google "change path windows" - experienced batchers create a separate directory on the path for batch files. Sometimes, they name the directory "Belfry".

How would I run a batch program from another batch program within its own environment?

I need to run a batch file located in another folder that must be called from another batch file.
Whenever I do call this batch file from the first, let's call them Batch_A and Batch_B, respectively, the second tries to run from the directory of the first batch file.
Batch_A needs to call or start Batch_B, however Batch_B needs to run as if I were to manually double-click it myself.
This is what I currently have at the end of my first batch
start "A thing" "%output%\thing.bat" /b
Have you looked into push or pop.
Before calling the second batch file, enter the "push" command:
pushd %dynamicdirectory%
Call batchfileb.bat
popd
If Batch_B is designed/written to be always run from the direcory where it is located
you might also consider to modify Batch_B.bat
setlocal
cd /D %0\..
REM your original content
endlocal
In %0 the path to the batchfile is stored.
The trick is to assume %0 is a directory then to change one level lower
based on that diretory.
With /D also the drive letter is changed correctly.
The cd command doesn't care if %0 is really a directory.
In fact %d doesn't even have to exist (%0\dummy\..\.. would also work).
The setlocal command is to have the working directory beeing restored
when Batch_B.bat has finished.
I noticed that the endlocal command is not really necessary
in this context since it is applied imlicitely when Batch_B finishes.

Running command-line in batch file

I have the following code which doesn't seem to be working properly - is someone able to assist with how to run command-lines in batch files
#echo off
set changeFrom=321
set changeTo=123
set origFile=config.txt
set newFile=config1.txt
test.bat %changeFrom% %changeTo% %origFile%>%newFile%
del %origFile%
ren %newFile% %origFile%
::end
I have a file "test.bat" which has code to replace strings in a file - but I Don't get how it can work ?
You need to use call to execute the second bat file from the first like this:
call test.bat %changeFrom% %changeTo% %origFile%>%newFile%
without call the first batch script will exit when the second one exits.

Setting a variable from an executable

I am running an executable in a batch file with two parameters;
cmd /k ""executable" "param1" "param2""
This returns a string that I want to launch. I can't figure out how to set this return in a variable and subsequently launch it in IE.
Any ideas?
If the returned string contains a single line you may use FOR /F to set the value of an environment variable. For example:
s1.cmd
echo this is a one line string
s2.cmd
#SETLOCAL
#ECHO OFF
for /f "tokens=*" %%a in ('cmd /c s1.cmd') do set MY_VAR=%%a
echo got: %MY_VAR%
ENDLOCAL
Result
C:\> s2.cmd
got: this is a one line string
C:\>
You can use the following syntax to capture the output of your executable into a variable:
FOR /F "tokens=*" %%i in ('%~dp0YOUR_APP.exe') do SET TOOLOUTPUT=%%i
Source
then you can pass the value on to IE like so:
START "YOUR_WINDOW_NAME" /MAX /D"C:\Program Files\Internet Explorer\" iexplore %TOOLOUTPUT%
I take it that the application code that determines the url is too complicated to be reproduced in a batch file directly, or the source to the executable has been lost. If not I personally would prefer to have the logic visible in the batch file itself.
start %1 %2
Edit: Romulo A. Ceccon posted a much better solution which doesn't involve any file system access and dirty tricks. Left this here for reference (it works with command.com as well if you need 9x compatibility), but please prefer Romulo's solution.
Go through an environment variable you set by using an intermediate helper script you dynamically generate from a template. You will need write permissions somewhere, otherwise it cannot be done (the Windows command shell language is very, very limited.)
Let's call your helper script template helper.tpl with the following contents:
set INTERMEDVAR=
Make sure that helper.tpl has only a single line (no trailing CRLF!) and make sure you don't have any spaces after the equals sign there.
Now, in your main script, capture the output from your command into a temporary file (let's call it my_output_file.tmp):
cmd /k ""executable" "param1" "param2"" > my_output_file.tmp
Then copy the contents of the helper template and the output together into your helper script, let's call it my_helper_script.cmd:
copy /b helper.tpl + my_output_file.tmp my_helper_script.cmd
Then evaluate the helper script in the current context:
call my_helper_script.cmd
Now the INTERMEDVAR variable is set to the first line of the output from "executable" (if it outputs more than one line, you're on your own...) You can now invoke IE:
start iexplore.exe "%INTERMEDVAR%"
And don't forget to clean up the created files:
del /q /f my_output_file.tmp my_helper_script.cmd
This will obviously not work when invoked multiple times in parallel - you'll have to parametrize the temporary file and helper script names using the current cmd.exe's PID (for example) so that they won't overwrite each other's output, but the principle is the same.
However, if you can get a real shell, use that. cmd.exe is extremely cumbersome.

Resources