**Batch file **I am writing batch file that takes in command line parameters and do some stuff if the parameters match. But when the run the batch file again with the new parameters, the file still runs with the old parameters.
first run : file.bat -name hello -w 400 -r 320
second run : file.bat -name hello -w 400.
When i do the second run of the file, it still performs the first run, i mean to say it still considers the parameters from the first run. Does anybody know why this is the case?
Thanks in advance.
Sorry for not posting the batch file code.
below mentioned is the code.
set filename=
set rate=
set hidden=
set rate=
:recheck
if "%1"=="-help" goto :help
if "%1"=="-name" goto :filename
if "%1"=="-h" goto :hidden
if "%1"=="-w" goto :weight
if "%1"=="-r" goto :rate
if "%~1"=="" goto :endofcommandline
:filename
shift
set filename =%1
shift
goto :recheck
For the other part of the file, I use the variable "filename" to do other stuff. I also use setlocal and endlocal at the beginning and the end of the file.
I have other corresponding labels for the each if statement and set different variables to the values passed to them if any by the user.
Sometimes, I also get echo is off when I try to print out the value of some variable.
All the variables which I use are cleared in the beginning of the file as mentioned above.
There are a couple points here.
"I also use setlocal and endlocal at the beginning and the end of the file."
"All the variables which I use are cleared in the beginning of the file as mentioned above."
Setlocal command does NOT clear the variables, it preserve current variables from posterior modification. This mean that if in a previous execution of your Batch file, or even via typed SET commands, filename variable was defined, it value remains until it is deleted or modified.
"Ok!", you said, "filename variable is modified the second time the Batch file run, Right?"
Well, no... The following line:
set filename =%1
does NOT modify "filename" variable, but "filename " instead (filename and a space). Try this:
set filename=No space
echo %filename%
set filename =Bad name with trailing space
echo %filename %
echo %filename%
You must carefully check your program to avoid this type of errors.
Related
I am writing a batch script for the 6th assignment in my class and I have hit a snag when I am pretty much finished. (We usually focus on bash scripting, so I am new to batch)
The script functions as desired the first time running it; but behaves differently when running it for the second time.
Essentially, the script checks the value of an argument if it exists and runs specific code depending on the value. For example, if the argument is "1", then it checks the PATH variable for a directory and creates it if it doesn't exist, and if it does exist - nothing happens and it just continues the script.
The issue occurs when running the script for a second time after the PATH is modified. I receive the output "\Common was not expected at this time".
I had a similar issue when running the script the first time, but managed to fix it by including Quotation marks on both side of the evaluation on the IF statement, but now I am unsure of where to continue with this.
My code is as follows:
#echo on
IF "%1%" == "0" (
SET "VAR1=%path%"
echo.%VAR1%|findstr /C:"App0" >nul 2>&1
if errorlevel 1 SET "PATH=%PATH%%cd%\App0;"
if not errorlevel 1 echo Found
goto errorBypass
) ELSE IF "%1%" == "1" (
SET "VAR2=%path%"
ECHO %VAR2%
echo.%VAR2%|findstr /C:"App1" >nul 2>&1
if errorlevel 1 SET "PATH=%PATH%%cd%\App1;"
if not errorlevel 1 echo Found
goto errorBypass
) ELSE IF "%1%" == "" (
IF "%HUMBER_HOME%" == "" (
goto Error2
) ELSE (
CALL "HUMBER_HOME\bin\setEnv.bat"
goto errorBypass
)
)
echo HERE
:Error2
echo Error2
:errorBypass
call "run.bat"
Also, so I know for future reference - is there an effective way to debug by going line by line? or a command that can output the particular line where the error occurred? I find it somewhat difficult when one error can be caused by multiple issues in different places.
Referencing a batch file argument
Open a command prompt window and run call /?. The output help explains how to reference batch file arguments. %1 references the first argument as passed to the batch file. That can be for example 1 (not quoted argument string), but also "1" (quoted argument string). %~1 references first argument string with surrounding double quotes removed.
It is wrong to add one more % after the argument reference. The syntax %variable% is used to reference the string value of an environment variable. Batch file arguments are referenced only with percent sign and digit without or with a modifier between. There is no more percent sign after the digit. That is also the reason why 1, 2, 3, ... are not possible as names for environment variables.
So not good is IF "%1%" == "0" ( because of this can result on batch file called with "1" as first argument in execution of the command line:
IF ""1"" == "0" (
The much better syntax is IF "%~1" == "0" ( which results in execution of the command line:
IF "1" == "0" (
See my answer on difference between “…” and x“…” in batch for more details on how to evaluate batch file arguments.
Appending a folder path to local PATH
The environment variable PATH holds a comma-separated list of folder paths whereby the list separator is a semicolon instead of a comma.
Therefore a ; at end of PATH means there is one more folder path which is an empty folder path. It is possible to specify an empty folder path in the middle or at end of PATH, but it is bad practice to do so because of PATH should not contain empty folder paths.
For that reason the following command line in your code is not good:
if errorlevel 1 SET "PATH=%PATH%%cd%\App0;"
There is also missing ; in case of PATH does not already end with a semicolon which could be a reason for the error message on second execution of the batch file.
The better code can be seen below on completely revised batch file code.
Referencing current directory
It is possible to reference the current directory with %CD% which can differ from batch file directory referenced with %~dp0. %~dp0 references drive and path of argument 0 which is the batch file itself. The batch file path string referenced with %~dp0 always ends with a backslash. Therefore no additional backslash should be used after %~dp0 on concatenating it with a file/folder name.
The dynamic environment variable CD ends usually with no backslash at end. So in most cases %CD% must be concatenated with an additional \ with a file/folder name. But there is one exception which must be taken into account on using %CD% in a batch file: %CD% expands to a string with \ at end on current directory being the root directory of a drive, for example C:\ or D:\. So it is always necessary on using %CD% to check if the string already ends with a backslash before appending a file/folder name without or with an additional backslash.
Other recommendations
The usage of a command block starting with ( and ending with ) should be avoided on using environment variables defined/modified within a command block and referenced within the command block as this requires the usage of delayed expansion as explained by help output on running set /? in a command prompt window on an IF and a FOR example on which command blocks are used usually. The Windows command processor is designed primary for executing one command line after the other. The usage of command blocks can speed up the execution of a batch file in some cases, but in many cases it is better to avoid them.
See debugging a batch file with a short description how to debug a batch file. A single step execution is not really possible. But cmd.exe shows on which line or command block an error occurred resulting in exiting batch file execution and what is the error.
Revised batch file code
Here is the revised batch file code:
#echo off
goto Main
:AddPath
echo %PATH%;|%SystemRoot%\System32\findstr.exe /I /C:"\%~1;" >nul 2>&1
if not errorlevel 1 echo Found %~1 in PATH& goto :EOF
set "Separator=;"
if "%PATH:~-1%" == ";" set "Separator="
if "%CD:~-1%" == "\" (set "AppPath=%CD%%~1") else set "AppPath=%CD%\%~1"
set "PATH=%PATH%%Separator%%AppPath%"
set "AppPath="
set "Separator="
goto :EOF
:Main
if "%~1" == "0" call :AddPath App0 & goto errorBypass
if "%~1" == "1" call :AddPath App1 & goto errorBypass
if not "%~1" == "" goto RunApp
if "%HUMBER_HOME%" == "" goto Error2
if exist "%HUMBER_HOME%\bin\setEnv.bat" (
call "%HUMBER_HOME%\bin\setEnv.bat"
goto errorBypass
)
echo File "setEnv.bat" in subdirectory "bin" in directory
echo defined by environment variable HUMBER_HOME not found.
echo HUMBER_HOME directory: "%HUMBER_HOME%"
echo/
pause
goto :EOF
:RunApp
echo HERE
goto :EOF
:Error2
echo Error2
goto :EOF
:errorBypass
if exist "run.bat" call "run.bat"
There is defined the subroutine AddPath at top of the batch file which is a bit unusual. So the second line with goto Main results in jumping over code of the subroutine on starting the execution of the batch file.
The subroutine AddPath is called with either App0 or App1 on first argument being 0 or "0" or 1 or "1".
The first line in AddPath outputs current value of local environment variable PATH with a semicolon appended and redirects this output to FINDSTR which searches case-insensitive and literally for the first argument string passed to the subroutine after a backslash and ending with a semicolon. The additional \ and ; should avoid a false positive on any folder path in PATH containing by chance also App0 or App1 somewhere in the middle of the folder path. This small enhancement is not 100% fail safe, but should be good enough.
FINDSTR exits with 0 on searched string found in line. In this case just an information message is output and the subroutine is exited which results in batch file execution being continued on main code on which the subroutine was called before. Otherwise the passed application name must be added to local PATH.
See also:
Where does GOTO :EOF return to?
Single line with multiple commands using Windows batch file
So first the environment variable Separator is defined with ; as value. But if local PATH already ends with a backslash although it should not, the environment variable is deleted immediately. Please note that the command line comparing last character of PATH with ; can fail if PATH ends with ". So this simple version is not 100% fail safe.
Next the current directory path is concatenated with the passed application folder name without or with an additional backslash depending on current directory being root directory of a drive or a subdirectory of any directory.
Then the local PATH is extended with appending the application path according to passed argument without or with an additional semicolon before.
Finally the no longer needed environment variables Separator and AppPath are deleted before exiting the subroutine.
A main mistake in main code as posted in question are the missing percent signs around environment variable HUMBER_HOME on calling batch file setEnv.bat in subdirectory bin of the directory assigned to environment variable HUMBER_HOME. This could be another reason for the error message on second execution of the batch file.
The revised code first checks if each batch file to call really exists before calling it.
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.
call /?
echo /?
findstr /?
goto /?
if /?
pause /?
set /?
I have a directory with for example 5 text files.
The batch file should perform a task on a text file and delete the file afterwards.
Is there a method to do this in a loop until there are no text files anymore?
By searching in world wide web I have found some code to count the number of files in a directory which may be a good starting point.
#echo off
set count=0
for %%x in (*.txt) do set /a count+=1
echo %count%
endlocal
Pause
Is there a method to incorporate this code into a batch file type of loop?
That is indeed a good starting point! A few things to note additionally:
You can execute more than one command in a loop using the syntax
do (
REM do things here
)
When using a variable within the loop you set it in, you have to use DelayedExpansion. Have a look at it here on StackOverflow there are about 5 Million questions about, why a variable in batch is not set in a loop.
The parameter you are using (%%x in your case) contains valuable information about files if you are looping over them. They are listed over here.
You may not use any goto within that loop or else it will be broken! This includes comments with :: (which you should in general replace with REM).
For additional help have a look around SO and for example SS64. Also note that you can get more information about a command using either help <commandname> or <commandname> /?
Open a command prompt window and run for /?. The output help explains how to used command FOR to do something on files found in a directory or with numbers or on various strings read from somewhere.
You can use for your simple task:
#echo off
for %%I in (*.txt) do (
echo File: "%%~fI"
echo del "%%~fI"
)
pause
Replace the first line with command ECHO in command block of FOR by the command line(s) to use on found *.txt file which name is hold by case-sensitive loop variable I.
Remove echo on second line in command block if the batch file is working as expected by you.
Note: This batch file just outputs the name of each found *.txt file with full path and the command line which would be used for deleting the *.txt file without doing the deletion.
What you probably need is a while loop kind of solution. Unfortunately no built in windows command exists for a while loop but you can easily mock it yourself. A while loop is just something that executes some code as long as a condition is true. To evaluate a condition you have the IF command. To jump back to some code (or jump over some code) you have the goto command and labels of the form :labelName (nothing else can come on the same line as a label declaration). To jump to a label you can either use goto labelName , goto :labelName or even goto:labelName.
Your while loop can take 2 forms:
Each time first evaluate condition then execute code (the while-do loop): the loop will be of this form
:loop
Rem commands to prepare your condition if any come here
<commands_prepare_condition>
IF NOT <condition> goto :outsideLoop
Rem commands to loop over come here
<commands_loop>
goto :loop
:outsideLoop
<command_after_loop>
Each time first execute the code inside the loop then verify condition for the next round (the do-while loop):
:loop
Rem commands to loop over come here
<commands_loop>
Rem commands to prepare your condition if any, come here
<commands_prepare_condition>
IF <condition> goto :loop
<commands_after_loop>
Beware that in this case, the loop will always be executed at least one time even if the first time the condition is not met because the condition is verified after the execution of the code in the loop.
In each of the 2 cases:
replace <commands_prepare_condition> with commands that will prepare your condition. As the list of conditions is limited for the IF you may need to prepare some variables or others before. In your case: the count of files.
replace <condition> with the condition you want to be true from the possibilities on this page. In your case that would be: %count% GTR 0. But consider #aschipfl's comment and use EXIST "*.txt" which doesn't need any preparation and will turn to true only if any file with txt extension exists
put all commands you want to execute while looping instead of <commands_loop>
<commands_after_loop> is the place where the commands after the loop must go. That part will be executed when the loop is exited.
I'm building a script for Windows command line in which I try to check some filenames in a FOR loop, and then stripping off part of the filename into a variable for further use. Basically, what I want to happen is this:
List all files in a certain directory, splitting of the extension like .osm.pbf in this case.
Assign the filename to a variable.
Out the last 7 characters of the filename in another variable.
Compare this new variable to "-latest".
If the compare is true, cut a part of the variable containing the filename.
If the compare is false, take over the complete variable into another variable.
Through some trial and error and some searching online, I've arrived at this point (which still isn't doing what I want):
FOR /F "tokens=1-2 delims=." %%M IN ('DIR /b %VECTOR_WORKDIR%\*.osm.pbf') DO (
SET VECTOR_CURRENT_MAP2=%%M
ECHO !VECTOR_CURRENT_MAP2! >> %VECTOR_LOGFILE%
SET LAST_BIT_TEMP=!VECTOR_CURRENT_MAP2:~-7!
ECHO !LAST_BIT_TEMP! >> %VECTOR_LOGFILE%
SET LAST_BIT=!LAST_BIT_TEMP: =!
ECHO !LAST_BIT! >> %VECTOR_LOGFILE%
IF !LAST_BIT!=="-latest" (
SET VECTOR_CURRENT_MAP3=!VECTOR_CURRENT_MAP2:~0,-8!
ELSE
SET VECTOR_CURRENT_MAP3=!VECTOR_CURRENT_MAP2!
)
ECHO !VECTOR_CURRENT_MAP3! >> %VECTOR_LOGFILE%
)
This results in these lines in the log file, for the file basse-normandie-latest.osm.pbf:
basse-normandie-latest
-latest
-latest
ECHO is on.
The first echo is correct, although the filename has a trailing space. (So actually it's "basse-normandie-latest ".)
The second echo doesn't seem to take this training space into account, as it correctly gives "-latest" as the last 7 characters. This echo also has a trailing space (So actually it's "-latest ".)
The third echo is an attempt to clear the spaces from the variable (by using ": ="), but this results in another trailing space. (So actually it's "latest ".)
The final echo after the IF statement (where I try to cut the "-latest" part from the filename), results in "ECHO is on".
I have SETLOCAL enabledelayedexpansion enableextensions declared at the top of my script.
Any thoughts on how to make this work, i.e. get rid of the trailing spaces to make the comparison work?
Thanks in advance for any pointers in the right direction!
A line like
ECHO !VECTOR_CURRENT_MAP2! >> %VECTOR_LOGFILE%
results in appending the value of the environment variable VECTOR_CURRENT_MAP2 to file with file name stored in environment variable VECTOR_LOGFILE with a trailing space because there is a space before redirection operator >> which is interpreted by Windows command processor as part of the string to output by command ECHO. This space must be removed to get the file name redirected into the log file without a trailing space.
In general it is critical on redirecting a variable string into a file without a space between the variable string and the redirection operator in case of the variable string ends with a space and a number being a valid handle number like 1 or 2 or 3. There are several solutions to workaround this problem like specifying the redirection left to command ECHO, i.e.
>>%VECTOR_LOGFILE% ECHO !VECTOR_CURRENT_MAP2!
But on using delayed expansion as simply necessary here, it is safe to append the redirection at end without a space between exclamation mark and >>, i.e.
ECHO !VECTOR_CURRENT_MAP2!>> %VECTOR_LOGFILE%
The space after redirection operator is ignored by Windows command processor and therefore can be kept although many batch file programmers (like me) with good syntax highlighting don't insert a space after a redirection operator.
On comparing strings with command IF and enclosing one string in double quotes which is always a good idea, it must be made sure that the other string is also enclosed in double quotes. The command IF does not remove the double quotes before comparing the strings. The double quotes are parts of the compared strings.
The condition
IF !LAST_BIT!=="-latest"
is only true if the string assigned to environment variable LAST_BIT would be with surrounding quotes which is never the case with your batch code and therefore the condition is never true.
Correct would be:
IF "!LAST_BIT!"=="-latest"
There is no need to use command DIR to search for files with a pattern in a directory as command FOR is designed for doing exactly this task. Processing of output of command DIR is an extension of FOR available only if command extensions are enabled as by default.
The file extension is defined by Microsoft as everything after last dot in name of a file. Therefore the file extension for your files is pbf respectively .pbf and .osm belongs to the file name.
Command FOR offers several modifiers to get specific parts of a file or directory name. Those modifiers are explained in help output into console window on running in a command prompt window for /?. Help of command CALL output with call /? explains the same for processing parameters of a batch file or subroutine (batch file embedded within a batch file).
Your code with all mistakes removed:
FOR %%M IN (*.osm.pbf) DO (
SET "VECTOR_CURRENT_MAP2=%%~nM"
SET "VECTOR_CURRENT_MAP2=!VECTOR_CURRENT_MAP2:~0,-4!"
ECHO !VECTOR_CURRENT_MAP2!>>%VECTOR_LOGFILE%
SET "LAST7CHARS=!VECTOR_CURRENT_MAP2:~-7!"
ECHO !LAST7CHARS!>>%VECTOR_LOGFILE%
IF "!LAST7CHARS!" == "-latest" (
SET "VECTOR_CURRENT_MAP3=!VECTOR_CURRENT_MAP2:~0,-7!"
) ELSE (
SET "VECTOR_CURRENT_MAP3=!VECTOR_CURRENT_MAP2!"
)
ECHO !VECTOR_CURRENT_MAP3!>>%VECTOR_LOGFILE%
)
Easier would be using this code with using string substitution feature of command SET, i.e. search within a string case-insensitive for all occurrences of a string and replace them with another string which can be also an empty string.
FOR %%M IN (*.osm.pbf) DO (
SET "VECTOR_CURRENT_MAP2=%%~nM"
SET "VECTOR_CURRENT_MAP2=!VECTOR_CURRENT_MAP2:~0,-4!"
ECHO !VECTOR_CURRENT_MAP2!>>%VECTOR_LOGFILE%
SET "VECTOR_CURRENT_MAP3=!VECTOR_CURRENT_MAP2:-latest=!"
ECHO !VECTOR_CURRENT_MAP3!>>%VECTOR_LOGFILE%
)
%%~nM is replaced on execution by Windows command processor by the name of the file without drive, path and file extension resulting for your example in basse-normandie-latest.osm.
The unwanted file name part .osm is removed with the next line in both batch code blocks which chops the last 4 characters from the file name 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 /?
if /?
set /?
Read the answer on question Why is no string output with 'echo %var%' after using 'set var = text' on command line? for an explanation why I used set "variable=value" on every line which assigns a value string to an environment variable because trailing whitespaces are critical for your task.
I have a scripting system driven by powershell which calls BAT files
I want to record the Envs at the end of the BAT session so that they are available for the next BAT file. I have found what I did, did not capture changes from the BAT file...
More
I had code like
& cmd.exe "/c $script $optionalArgs & (echo Name^=Value&SET) > ""c:\path\end_envs.csv"""
$script == dosomework.bat
which appeared to work, i.e. the csv file is created with env's BUT does not have the ones created by $script... Rather seem to be the ENVs of the initial cmd.exe call...
After adding the 'echo xx' lines to a BAT file and calling this at the bottom of the bat file referenced in $script, all is OK, I see the env at the end of the $script BAT file
Is there a way to fool a cmd.exe /c to consider my extra echo info as part of the called script ?
Thanks
OR...
sae this as $$script.bat
#ECHO OFF
(
echo Name=Value
CALL $script %*
SET
)>"c:\path\end_envs.csv"
and modify your code to like
& cmd.exe "/c $$script $optionalArgs"
whee $$script.bat may naturally be any name you like.
Note that this assumes that $script.bat (the one you are actually executing) does not contain a setlocal instruction.
If $script is variable, then in the batchfile $$script.bat try
#ECHO OFF
SETLOCAL
(
echo Name=Value
set "params=%* "
CALL %1 %%params:* =%%
set "params="
SET
)>"u:\envs.csv"
GOTO :EOF
This assumes there are no confounding factors (parameters containing characters to which cmd is sensitive, for instance - normal alphameric strings should be fine)
So
write out the header line
set params to the supplied parameter list + a space
execute a subroutine firstparameter with parameter(s) remainingparameters. The call ...%%params:* =%% syntax means "the value of params, up to and including the first space"
clear params
list the remining environment variables.
The destination filename is of course up to you. I used a name that's convenient for my system.
So - you would call this script with a parameter of (the first name of the script you want to run) + (any other parameters you require)
Simplest would probably be to add
(echo Name^=Value
SET) > "c:\path\end_envs.csv"
to the end of $script (or at least, force those two lines to be executed just prior to exit)
I have easy .bat file
%2 >file.tmp
set /p %1=<file.tmp
I expect to use this script in this way:
putVar var1 command.bat
It should get first line of output command.bat into variable var1
and after that I should be able use var1 variable, for example
putVar var1 command.bat
echo %var1%
Unfortunately this do not work.
How to get this work? Maybe there is some easier way?
Your problem is that in your sample code the command you are running (command.bat) is a batch file, which it is being invoked from inside a batch file, and if from a batch file you call another batch file without using call command, then the execution does not return to caller batch.
To keep the syntax, you can use
(cmd /c "%~2")>file.tmp
set /p "%~1="<file.tmp
spawning a second instance of cmd to handle the command, redirecting its output to the temporary file and then reading the file (only the first line of command.bat output) into the variable.
A simpler way of doing it is to use for /f
for /f "delims=" %%a in ('%~2') do set "%~1=%%a"
which, more or less, do the same work. Run the command retrieve the output and, in this case, iterate over the output of the command, assigning each of the lines of command.bat output to the variable. Obviously, only the last line will be stored inside the variable, as the loop will overwrite its value in each iteration.