I want a batch file that opens and:
Sets all items in list.txt to variable v one at a time.
For each of the items in list.txt to open a command prompt and run starter.bat
Carry variable v into starter.bat
My code
SETLOCAL EnableDelayedExpansion
FOR /F "tokens=*" %%v in (C:\users\anhall\desktop\test\list.txt) DO START c:\users\anhall\desktop\test\starter.bat
ENDLOCAL & SET computer=%v%
this is just an example of what will be in there, when I get it all figured out there should be a couple hundred items in the list. But I will probably break it down into smaller lists for ease of running.
list.txt
nhn-0073
nhn-0115
nhn-0846
This is what I end up with on the primary window:
I can see that its not working because it doesn't even carry to ENDLOCAL & SET computer=
My main concern is carrying the variable into each of the new windows. I know this is possible but I can't get it to work.
Completed Code
My code
SETLOCAL
FOR /F "tokens=*" %%v in (C:\users\anhall\desktop\test\list.txt) DO START c:\users\anhall\desktop\test\starter.bat %%v
changes to starter.bat
changed variable to %1
Just pass it in on the command line:
for /F %%v in (YourFile.txt) do start c:\users\anhall\desktop\test\starter.bat %%v
Modify your starter.bat file to use %1 to receive the variable from the command line.
:: Starter.bat - replace echo with your actual command
#echo %1
(Doing it from the command line means you never change the computer environmental variable, so you can remove the & SET at the end of your first batch file.)
Related
This question already has answers here:
Arrays, linked lists and other data structures in cmd.exe (batch) script
(11 answers)
Closed 3 years ago.
I have a list of paths from which I want to extract folder name
I wrote:
#echo off
set paths[0]="C:\p\test1"
set paths[1]="C:\p\test2"
set paths[2]="C:\p\test3"
(for %%p in (%paths%) do (
for %%F in (%%p) do echo Processing %%~nxF
))
but seems that nothing is shown.
I expected to see:
Processing test1
Processing test2
Processing test3
It makes a big difference if first " is specified on a set command line left to variable name or left to variable value. In most cases it is better to specify it left to the variable name, especially if a variable value holding a path should be concatenated later with a file name to a full qualified file name.
See also: Why is no string output with 'echo %var%' after using 'set var = text' on command line?
The solution for this task is:
#echo off
set "paths[0]=C:\p\test1"
set "paths[1]=C:\p\test2"
set "paths[2]=C:\p\test3"
for /F "tokens=1* delims==" %%I in ('set paths[ 2^>nul') do echo Processing %%~nxJ
The command FOR with option /F and a set enclosed in ' results in starting one more command process running in background with %ComSpec% /c and the command line specified between the two ' appended as further arguments. So executed is in this case with Windows installed to C:\Windows:
C:\Windows\System32\cmd.exe /c set paths[ 2>nul
The command SET outputs all environment variables of which name starts with paths[ line by line using the format VariableName=VariableValue to handle STDOUT of started background command process.
It could be that there is no environment variable of which name starts with paths[ which would result in an error message output to handle STDERR by command SET which would be redirected from background command process to handle STDERR of the command process which is processing the batch file and for that reason would be displayed in console window. For that reason a possible error message is redirected by the background command process to device NUL to suppress it with using 2>nul.
Read the Microsoft article about Using command redirection operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded set command line with using a separate command process started in background.
FOR captures in this case everything written to handle STDOUT of started background command process and process this output line by line after started cmd.exe terminated itself.
Empty lines are ignored by FOR which does not matter here as there are no empty lines to process.
FOR would split up a non-empty line into substrings using normal space and horizontal tab as string delimiters and would assign just first space/tab separated string to specified loop variable, if it does not start with default end of line character ;. This default line splitting behavior is not wanted here. For that reason the option delims== defines the equal sign as string delimiter.
The option tokens=1* instructs FOR to assign in this case the variable name to specified loop variable I and assign everything after the equal sign(s) after variable name without any further string splitting on equal signs to next loop variable according to ASCII table which is in this case J. That is the reason why loop variables are interpreted case-sensitive while environment variables are handled case-insensitive by the Windows command processor.
In this case only the variable value is of interest in the body of the FOR loop. For that reason just loop variable J is used on ECHO command line while I is not used at all.
The modifier %~nxJ results in removing surrounding double quotes from string value assigned to loop variable J and next get the string after last backslash or beginning of string in case of the string value does not contain a backslash at all. This is the name of the last folder in folder path string.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
for /?
set /?
UPDATE:
There is a big advantage of this solution in comparison to the other two solutions posted up to now here:
There is not used delayed environment variable expansion which is always problematic on working with file or folder names on not being 100% sure that no folder and no file contains ever an exclamation mark in its name.
Let us compare the three solutions with unusual folder names containing !.
#echo off
rem Make sure there is no environment variable defined of which name starts with
rem paths[ as suggested by Compo which is a very valuable addition on my code.
for /F "delims==" %%I in ('set paths[ 2^>nul') do set "%%I="
set "paths[0]=C:\p\test1!"
set "paths[1]=C:\p\!test2"
set "paths[2]=C:\p\!test!3"
echo/
echo Results of solution 1:
echo/
for /F "tokens=1* delims==" %%I in ('set paths[ 2^>nul') do echo Processing %%~nxJ
echo/
echo Results of solution 2:
echo/
SetLocal EnableDelayedExpansion
for /L %%i in (0,1,2) do (
for %%j in (!paths[%%i]!) do echo Processing %%~nxj
)
endLocal
echo/
echo Results of solution 3:
echo/
Setlocal EnableDelayedExpansion
Call :process paths "!paths[0]!" "!paths[1]!" "!paths[2]!"
Endlocal
echo/
pause
goto :EOF
:process
Set P_C=0
Set /a P_C-=1
For %%a in (%*) DO (
CALL :populate %1 "%%~a"
)
Set /a P_C-=1
For /L %%b in (0,1,!P_C!) DO (
ECHO Processing %1[%%b] = "!%1[%%b]!"
)
GOTO :EOF
:populate
Set "%1[!P_C!]=%~2"
Set /a P_C+=1
GOTO :EOF
The output on running this batch file is:
Results of solution 1:
Processing test1!
Processing !test2
Processing !test!3
Results of solution 2:
Processing test1
Processing test2
Processing 3
Results of solution 3:
Processing paths[0] = "C:\p\test1\p\\p\3"
Solution 1 as posted here works for all three folder names correct.
Solution 2 omits for first and second folder name the exclamation mark which will most likely cause errors on further processing. The third folder name is modified to something completely different. Enabled delayed expansion results in parsing a second time echo Processing %%~nxj after %~nxj being replaced by !test!3 with interpreting test in folder name now as environment variable name of which value is referenced delayed. There was no environment variable test defined on running this batch file and so !test!3 became just 3 before echo was executed by Windows command processor.
Solution 3 produces garbage on any folder name contains an exclamation mark, even on full qualified folder name defined before enabling delayed expansion and referenced with delayed expansion on calling the subroutine process.
Well, folder and file names with an exclamation mark in name are fortunately rare which makes the usage of delayed expansion usually no problem. But I want to mention here nevertheless the potential problems which could occur on any folder name containing one or more !.
Something like that should work :
#echo off
set paths[0]="C:\p\test1"
set paths[1]="C:\p\test2"
set paths[2]="C:\p\test3"
SetLocal EnableDelayedExpansion
for /L %%i in (0,1,2) do (
for %%j in (!paths[%%i]!) do echo Processing %%~nxj
)
pause
Define the Array within the function.
This approach can be used to define multiplay Arrays.
#ECHO OFF
Setlocal EnableDelayedExpansion
:: REM P_C is used to define the range of the Array. The -1 operations on P_C is to shift the paths parameter out of the Arrays working Index.
::REM the first parameter passed is used as the Arrays Name. all other parameters are assigned to index values 0 +
Call :process paths "C:\p\test1" "C:\p\test2" "C:\p\test3"
pause
:process
Set P_C=0
Set /a P_C-=1
For %%a in (%*) DO (
CALL :populate %1 "%%~a"
)
Set /a P_C-=1
For /L %%b in (0,1,!P_C!) DO (
ECHO Processing %1[%%b] = "!%1[%%b]!"
)
GOTO :EOF
:populate
Set "%1[!P_C!]=%~2"
Set /a P_C+=1
GOTO :EOF
I'm trying to use CMDER for a development environment that I've setup.
Basically I've created a .bat file that calls:
#ECHO OFF
start Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\CMDER\Cmder.exe
Then I've placed the file startdev.bat in:
%CMDER_HOME%\config\profile.d
So everything seems to work just fine, but when the startdev.bat finishes, issuing an:
echo %PATH%
returns:
Z:\_DEV\OS_WINDOWS\1_COMPILER\JDK\ORACLE\1.8.0_181\bin;Z:\_DEV\OS_CYGWIN\bin;Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\CLutils;Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\PUTTY;Z:\_DEV\OS_WINDOWS\6_VERSION_CONTROL\PortableGit\bin;C:\WINDOWS;C:\WINDOWS\SysWOW64;C:\WINDOWS\System32
...any idea what's happening?
I would either expect CMDER to override PATH with the value from its own settings, or use my full path, which before the startdev.bat ends shows the value of:
PATH=Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\CMDER\vendor\conemu-maximus5;Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\CMDER\vendor\conemu-maximus5\ConEmu;Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\CMDER\vendor\conemu-maximus5\ConEmu\Scripts;Z:\_DEV\OS_ALL\JVM\3_BUILD_TOOLS\GRADLE\5.4\bin;Z:\_DEV\OS_ALL\JVM\3_BUILD_TOOLS\MAVEN\3.5.4\bin;Z:\_DEV\OS_ALL\JVM\3_BUILD_TOOLS\ANT\1.10.5\bin;Z:\_DEV\OS_WINDOWS\3_BUILD_TOOLS\NODE\LTS\10.15.3;Z:\_DEV\OS_WINDOWS\3_BUILD_TOOLS\NODE\LTS\10.15.3\node_modules;Z:\_DEV\OS_WINDOWS\1_COMPILER\GO\1.12.4\bin;Z:\_DEV\OS_WINDOWS\1_COMPILER\PYTHON\32bit\2.7.13;Z:\_DEV\OS_WINDOWS\1_COMPILER\PYTHON\32bit\2.7.13\scripts;Z:\_DEV\OS_WINDOWS\1_COMPILER\ANDROID\android-sdk-windows\platform-tools;Z:\_DEV\OS_WINDOWS\1_COMPILER\JDK\ORACLE\1.8.0_181\bin;Z:\_DEV\OS_CYGWIN\bin;Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\CLutils;Z:\_DEV\OS_WINDOWS\9_MISC_TOOLS\PUTTY;Z:\_DEV\OS_WINDOWS\6_VERSION_CONTROL\PortableGit\bin;C:\WINDOWS;C:\WINDOWS\SysWOW64;C:\WINDOWS\System32
..but the fact that it only seems to be keeping the value as defined about halfway through the batch job is strange.
Any ideas?
First I recommend opening a command prompt window and run setlocal /? and endlocal /? to get displayed the help/documentation for those two commands. Very important to know is that every setlocal without a corresponding endlocal results in an implicit execution of endlocal by cmd.exe before exiting processing of a batch file or a subroutine called with command CALL.
Next I suggest reading this answer for even more details about the commands SETLOCAL and ENDLOCAL and what happens on using them.
I suggest like michael_heath to change this code block:
setLocal EnableDelayedExpansion
set CLASSPATH=.
for /R %JRE_HOME%\lib %%a in (*.jar) do (
set CLASSPATH=!CLASSPATH!;%%a
)
set CLASSPATH=!CLASSPATH!
Better would be:
setLocal EnableExtensions EnableDelayedExpansion
set CLASSPATH=.
for /R "%JRE_HOME%\lib" %%a in (*.jar) do set "CLASSPATH=!CLASSPATH!;%%a"
endlocal & set "CLASSPATH=%CLASSPATH%"
Now the local environment is ended with passing the environment variable CLASSPATH from local environment, on which it was defined, to the restored previous environment because of cmd.exe expands %CLASSPATH% to current value of the environment variable CLASSPATH in current local environment before executing the command endlocal which restores the previous environment.
Wrong in your batch file is also set WINDIR=%SystemRoot%;%SystemRoot% which should be set "WINDIR=%SystemRoot%".
I recommend further reading Why is no string output with 'echo %var%' after using 'set var = text' on command line? It explains why the syntax set "variable=string value" is recommended nowadays. Many of the environment variable definitions use directly or indirectly %UserProfile% which means depending on whatever the user currently running the batch file has entered as user name on creation of the user account. I have seen users entering their name containing a space and non ASCII characters. And I have seen users creating an account with a user name containing character & like Company GmbH & Co. An ampersand outside a double quoted argument string is interpreted as AND operator and cmd.exe tries to execute after set also the remaining string after & as command line on using something like set USERHOME=%DEVHOME%\%USERNAME% instead of set "USERHOME=%DEVHOME%\%USERNAME%". Well, startdev.bat redefines nearly all predefined Windows Environment Variables including USERNAME and USERPROFILE and so is written safe for most environment variable definitions.
This code block is also not optimal:
FOR /F "usebackq" %%i IN (`hostname`) DO SET HOSTNAME=%%i
echo Running on hostname: %HOSTNAME%
The host name respectively computer name could contain also a space or characters critical for command line or start with a semicolon for some unknown reason. So better would be:
FOR /F delims^=^ eol^= %%i IN ('hostname') DO SET "HOSTNAME=%%i"
setlocal EnableDelayedExpansion & echo Running on host name: !HOSTNAME!& endlocal
Whereby there is the environment variable COMPUTERNAME predefined by Windows making it possible to use just following command line:
setlocal EnableDelayedExpansion & echo Running on host name: !ComputerName!& endlocal
An ECHO command line containing an immediately expanded environment variable reference on which it is unknown if its value contains &|<> is always a problem because of the environment variable reference is expanded before further processing of the command line by cmd.exe as described at How does the Windows Command Interpreter (CMD.EXE) parse scripts?
I suggest also reading DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/ and avoid the usage of echo. in the batch file to output an empty line.
"halfway through the batch job" as you have a
setLocal EnableDelayedExpansion which sets any further
changes to the variable PATH or other set variables as local.
The endLocal not specified is implied at the end of the script.
To resolve this, use endLocal and set CLASSPATH=%CLASSPATH%
on the same parsed line to set CLASSPATH as global.
Change this part:
setLocal EnableDelayedExpansion
set CLASSPATH=.
for /R %JRE_HOME%\lib %%a in (*.jar) do (
set CLASSPATH=!CLASSPATH!;%%a
)
set CLASSPATH=!CLASSPATH!
to this:
setLocal EnableDelayedExpansion
set CLASSPATH=.
for /R %JRE_HOME%\lib %%a in (*.jar) do (
set CLASSPATH=!CLASSPATH!;%%a
)
endLocal & set CLASSPATH=%CLASSPATH%
After that changed part, the script will set variables as global again.
I am trying to create a batch file:
The batch file will locate the path of executable first. Then, the path will be stored in a variable for later use.
This is my code:
#echo off
setlocal
set directoryName=dir/s c:\ABCD.exe
rem run command
cmd /c %directoryName%
pause
endlocal
The command prompt does return me with the executable's path but the path is not stored in the variable. Why is it so?
Reading your question, it appears that you're not really wanting to save the path of the executable file at all, but the file name complete with it's full path:
I prefer the Where command for this type of search, this example searches the drive in which the current directory resides:
#Echo Off
Set "mPth="
For /F "Delims=" %%A In ('Where /R \ "ABCD.exe" /F 2^>Nul'
) Do Set "mPth=%%A"
If Not Defined mPth Exit /B
Rem Rest of code goes here
The variable %mPth% should contain what you need. I have designed it to automatically enclose the variable value in doublequotes, if you wish to not have those, change %%A on line 4 to %%~A. If the file is not found then the script will just Exit, if you wish it to do something else then you can add that functionality on line 5.
Note: the code could find more than one match, if it does it will save the variable value to the last one matched, which may not be the one you intended. A robust solution might want to include for this possibility.
Edit (this sets the variable, %mPth% to the path of the executable file only)
#Echo Off
Set "mPth="
For /F "Delims=" %%A In ('Where /R \ "ABCD.exe" /F 2^>Nul'
) Do Set "mPth=%%~dpA"
If Not Defined mPth Exit /B
Set "mPth=%mPth:~,-1%"
Rem Rest of code goes here
Lets walk through your code
set directoryName=dir/s c:\ABCD.exe
This fills the variable directory name with the value dir/s c:\ABCD.exe.
cmd /c %directoryName%
This executes the command in directoryname. There is no line in your code that saves the files location to a variable.
Extracting the path of a file can be done as follows
#echo off
setlocal
set executable=c:\location\ABCD.exe
FOR %%A IN ("%executable%") DO Set executablepath=%%~dpA
echo executablepath: %executablepath%
pause
endlocal
%executablepath% will contain c:\location\
The value that you are assigning to directoryname is dir /s c:\abc.exe.
this value is then substituted for %directoryname% in your cmd line, which executes the command dir/s..., showing you the location(s) of abc.exe in the familiar dir format.
If what you want is just the directoryname in directoryname, then you need
for /f "delims=" %%a in ('dir /s /B /a-d "c:\abc.exe"') do set "directoryname=%%~dpa"
which will first execute the dir command, then process each line of output from that command and assign it in its entirety to %%a.
The dir command shown would "display" the matching names found in the nominated directory (c:\) and its subdirectories (/s) in basic form (/b) - that is, names only, no size or date or report-headers or report-footers, and a-d without directorynames (should they match the "mask" abc.exe)
The delims= option to the for /f command instructs that the entire line as output by the command in single-quotes, be assigned to %%a.
When the result is assigned to the variable directoryname, only the Drive and Path parts are selected by using the ~dp prefix the the a.
Note that only the very last name found will be assigned to the variable as any earlier assignment will be overwritten by a succeeding assignment.
This may or may not be what you are looking for. This script searches through the PATH variable and looks for files that have and extension in the PATHEXT variable list.
#SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
#SET EXITCODE=1
:: Needs an argument.
#IF "x%1"=="x" (
#ECHO Usage: %0 ^<progName^>
GOTO TheEnd
)
#set newline=^
#REM Previous two (2) blank lines are required. Do not change!
#REM Ensure that the current working directory is first
#REM because that is where DOS looks first.
#PATH=.;!PATH!
#FOR /F "tokens=*" %%i in ("%PATH:;=!newline!%") DO #(
#IF EXIST %%i\%1 (
#ECHO %%i\%1
#SET EXITCODE=0
)
#FOR /F "tokens=*" %%x in ("%PATHEXT:;=!newline!%") DO #(
#IF EXIST %%i\%1%%x (
#ECHO %%i\%1%%x
#SET EXITCODE=0
)
)
)
:TheEnd
#EXIT /B %EXITCODE%
Note that this may find multiple executables. It may also find multiple types of executables. The current directory is also included first since that is what the shell, cmd.exe, does.
M:>whence wc
.\wc.BAT
.\wc.VBS
C:\Users\lit\bin\wc.BAT
C:\Users\lit\bin\wc.VBS
I need some batch-noob-treatment.
I'm trying to rename all the files in a folder and I'm slowly getting there, but although I got the step right with some google help, I would like to know what I'm doing.
Here is my code until now:
#ECHO OFF
setlocal enabledelayedexpansion
set a="dir /b"
FOR /F "delims=" %%i IN (' %a% ') DO (
set str=%%i
set str=!str:~1!
echo !str!
)
Anything else I put inside the for loop brings out strange results. I know the enabledelayedexpansion and ! are somehow connected and make this work, but how is the inside of a for loop different from the normal commands in batch, so that special syntax is needed?
What goes wrong if I put in code like that:
#ECHO OFF
set a="dir /b"
FOR /F "delims=" %%i IN (' %a% ') DO (
set i=%i:~1%
echo %%i
)
set i=%i:~1% is wrong as there is a gigantic difference between for loop control variables (e.g. %i) versus environment variables (e.g. %i% or %str%) and appropriate manipulation with them:
(%~i, %~1 etc. special page) Command line arguments and FOR parameters
(%variable:~num_chars_to_skip,num_chars_to_keep% etc.) Extract part of a variable (substring)
(%variable:StrToFind=NewStr% etc.) Variable Edit/Replace
Further resources (required reading, incomplete):
(command reference) An A-Z Index of the Windows CMD command line
(additional particularities) Windows CMD Shell Command Line Syntax
(special page) EnableDelayedExpansion
How do I write a batch file which gets an input variable and sends it to another batch file to be processed.
Batch 1
I don't know how to send a variable to batch 2 which is my problem here.
Batch 2
if %variable%==1 goto Example
goto :EOF
:Example
echo YaY
You don't need to do anything at all. Variables set in a batch file are visible in a batch file that it calls.
Example
test1.bat
#echo off
set x=7
call test2.bat
set x=3
call test2.bat
pause
test2.bat
echo In test2.bat with x = %x%.
Output
... when test1.bat runs.
In test2.bat with x = 7.
In test2.bat with x = 3.
Press any key to continue . . .
You can pass in the batch1.bat variables as arguments to batch2.bat.
arg_batch1.bat
#echo off
cls
set file_var1=world
set file_var2=%computername%
call arg_batch2.bat %file_var1% %file_var2%
:: Note that after batch2.bat runs, the flow returns here, but since there's
:: nothing left to run, the code ends, giving the appearance of ending with
:: batch2.bat being the end of the code.
arg_batch2.bat
#echo off
:: There should really be error checking here to ensure a
:: valid string is passed, but this is just an example.
set arg1=%~1
set arg2=%~2
echo Hello, %arg1%! My name is %arg2%.
If you need to run the scripts simultaneously, you can use a temporary file.
file_batch1.bat
#echo off
set var=world
:: Store the variable name and value in the form var=value
:: > will overwrite any existing data in args.txt, use >> to add to the end
echo var1=world>args.txt
echo var2=%COMPUTERNAME%>>args.txt
call file_batch2.bat
file_batch2.bat
#echo off
cls
:: Get the variable value from args.txt
:: Again, there is ideally some error checking here, but this is an example
:: Set no delimiters so that the entire line is processed at once
for /f "delims=" %%A in (args.txt) do (
set %%A
)
echo Hello, %var1%! My name is %var2%.
If you are reading the answers and still getting problems, you may be using setlocal wrong.
setlocal works like namespaces with endlocal closing the last opened namespace.
If you put endlocal at the end of your callee as I used to do by default, your variable will be lost in the previously opened setlocal.
As long as your variable is set in the same "local" as your caller file's you will be ok.
Here are 2 ways. One writes to a file while the other does not.
All include: setlocal EnableDelayedExpansion
Batch1: (.bat or .cmd)
set myvar1=x 1920
set myvar2=y 1080
set myvar > MyConfig.ini
REM (Use >> to append file and preserve existing entries instead of overwriting.)
MyConfig.ini will be created/appended containing:
myvar1=x 1920
myvar2=y 1080
Batch2:
for /f "delims== tokens=1,2" %%I in (myconfig.ini) do set %%I=%%J
Then, myvar1 and myvar2 will be set to their stored values.
To avoid any disk file storage, try something I cooked up:
Batch1:
(may contain:) start /b cmd /c Batch2.bat
title set myvar1=x 1920^& set myvar2=y 1080
Batch2:
for /f "tokens=*" %%G in ('gettitle.exe') do (set titlenow=%%G)
%titlenow%
The result is that Batch2 runs the commands...
set myvar1=x 1920& set myvar2=y 1080
...to replicate the variables from Batch1.
The vars might then be changed and returned to Batch1 in the same manner.
It's ideal for parallel processing AI apps on supercomputers running Windows 10! lol
gettitle.exe is from http://www.robvanderwoude.com
You can find out the solution from below-
variable goes here >> "second file goes here.bat"
What this code does is that it writes the variables to the second file if existing.Even if it does not exist, it will create a new file.