set statements don't appear to work in my batch file - batch-file

I've found what appears to be an explanation of my problem here
DOS batch: Why are my set commands resulting in nothing getting stored?
but I don't really understand the explanation.
Here is my script...
for /R /d %%f in (\Product\Database\SQL\Project\Model\Scripts\*) DO (
REM echo %%f
SET !LOAD_FILE_FILTER= %%f\*.sql
echo file: %!LOAD_FILE_FILTER%
CALL %!BATCH_FILE% -u %!USER_NAME% -p %!PASSWORD% -s %!SERVER_NAME% -d %!DATABASE_NAME% -f %!LOAD_FILE_FILTER% -o %!LOG_FILE%
IF %!EchoErrors%==1 (
ECHO [
TYPE %!LOG_FILE%
ECHO ]
)
)
The echo always prints file: *.sql and the script I pass this var to always complains LOAD_FILE_FILTER is empty.
I have tried adding setlocal EnableDelayedExpansion as suggested in the article but it doesn't solve the problem. The echo file: %!LOAD_FILE_FILTER% always prints the last subdirectory in the directory I'm running from. The echo %%f always prints the correct value.
What does the '!' behind the variable do for/to me?
On a side note, could someone explain to me the difference between
SET !VAR
and
SET VAR
%VAR&
&!VAR&
!VAR!
%%VAR

We are going to start with a simple case
set "var="
set "var=test"
echo %var%
Reading the code, it removes the content of the variable, assigns it a new value and echoes it.
Let's change it a bit concatenating the last two commands
set "var="
set "var=test" & echo %var%
"Same" code, but in this case the output to console will not show the value in the variable.
Why? In batch files, lines to execute are parsed and then executed. During the parse phase, every variable read operation (where you retrieve the value of the variable) is replaced with the value stored inside the variable at parse time. Once this is done, the resulting command is executed. So, in the previous sample when the second line is parsed, it is converted to
set "var=test" & echo
now, there are no read operations on the line and no value to echo, as when the line was readed the variable didn't hold any value (it will be assigned when the line is executed) so the read operation has been replaced with nothing. At this point, the code is executed and the perceived behaviour is that the set command failed as we don't get the "obvious" value echoed to console.
This behaviour is also found in blocks. A block is a set of lines enclosed in parenthesis (usually for and if constructs) and are handled by the parser as if all the lines in the block are only one line with concatenated commands. The full block is readed, all variable read operations removed and replaced with the value inside the variables, and then the full block, with no variable references inside is executed.
At execution time there are no read operation on variables inside the block, only its initial values, so, any value assigned to a variable inside the block can not be retrieved inside the same block, as there isn't any read operation.
So, in this code
set "test=before"
if defined test (
set "test=after"
echo %test%
)
after the first set is executed, the block (the if command and all the code enclosed in its parenthesis) will be parsed and converted into
if defined test (
set "test=after"
echo before
)
showing the "wrong" value.
The usual way to deal with it is to use delayed expansion. It will allow you to change, where needed, the syntax to read the variable from %var% into !var!, indicating to the parser that the read operation must not be removed at parse time, but delayed until the command is executed.
setlocal enabledelayedexpansion
set "var="
set "var=test" & echo !var!
The now third line is converted at parse time to
set "var=test" & echo !var!
yes, the variable reference is not removed. The read operation is delayed until the echo command will be executed, when the value of the variable has been changed.
So
%var% is a variable reference that will be replaced at parse time
!var! is a variable reference that will be replaced at execution time
%x with x a single character is usually a for replaceable parameter, a variable that will hold the current element being interated. By its own nature, will be expanded at execution time. The syntax with a single percent sign is used at command line. Inside batch files the percent sign need to be escaped and the syntax to refer to the replaceable parameters is %%x

Related

Get a variable stored inside of another variable and get the data out of the variable

The title may be confusing but heres what im trying to do:
#echo off
set testvar=Hello
call :getvarout testvar
:getvarout
set varout=%~1
echo %%%varout%%%
pause
Expected Output:
Hello
Output:
%testvar%
Do you have in mind something similar to this?
#echo off
set testvar=Hello
call :getvarout testvar
goto end
:getvarout
set varout=%~1
call echo %%%varout%%%
pause
:end
#ECHO OFF
SETLOCAL
set "testvar=Hello"
call :getvarout testvar
ECHO varout value is "%varout%"
GOTO :eof
:getvarout
set varout=%~1
CALL SET "varout=%%%varout%%%"
GOTO :EOF
The object of the exercise is, I believe, to retrieve the value of a variable whose name is known - or whose name is contained in another variable, so
call :getvarout %testvar%
would retrieve the value of the variable hello.
For OP:
When you use the point-click-and-giggle method of executing a batch, the batch window will often close if a syntax-error is found. You should instead open a 'command prompt' and run your batch from there so that the window remains open and any error message will be displayed.
Use set "var1=data" for setting values - this avoids problems caused by trailing spaces. In comparisons, use if "thing1" == "thing2" ... to avoid problems caused by spaces in thing1/2.
On original code:
Unlike many languages, batch has no concept of the end of a "procedure" - it simply continues execution line-by-line until it reaches the end-of-file. Consequently, you need to goto :eof after completing the mainline, otherwise execution will continue through the subroutine code. :EOF is a predefined label understood by CMD to mean end of file. The colon is required.
you can either use delayed expansion to allow expansion of nested variables using like so:
Setlocal enabledelayedexpansion
Echo(!%varout%!
Or use the Call command in conjunction with Echo to force the parser to undergo an additional round of expansion using:
Call Echo(%%varout%%
The above call method is rather inefficient, however if the variables may contain the exclamation character it is an alternative method.
With the use of Call, A variable can be expanded at increasing levels of depth, until the variable is found to be empty. The limitation is in the line length limit, as the number of % expansions that need to be used to expand the variable increases proportionally to the number of Calls used to Parse over the variable for each level of expansion to be achieved.
The following rules apply to expansion depth using Call:
E (escaped % expansion) begins with a value of 1 and increases in proportion to C like so:
E = ( C * 4 ) -1
The value of C (Calls required to parse the Variable) commences at 0, increments by 1 for the 2nd expansion, Then doubles for Each subsequent Expansion

Problems with removing Trailing \ in batch file

I have a problem removing trailing \ in a script, my current script is:
echo on
setlocal enableextensions enabledelayedexpansion
SET SCRIPTFOLDER=C:\install$
FOR /F "tokens=* delims=," %%a in (%SCRIPTFOLDER%\GetFilesandFoldersFromHere.Txt) DO (
set data.path=%%~pa
SET data.path=%data.path:~0,-1%
echo %data.path%
rem echo file and folder= %%~na%%~xa Folder=%data.path%
)
The GetFilesandFoldersFromHere.Txt file has lines of files and location e.g.:
T:\First File Here\Move this File.txt
When I run the above code I get:
C:\install$\file Archive Scripts>(
set data.path=\First File Here\
rem If ~-1data.path:~0,0
SET data.path=~0,-1
echo
rem echo file and folder= Move this File.txt Folder=
I want to assign data.path the directory (without drive letter and the trailing ). It assigns the value but when I try to get rid of the trailing \ the value is nulled.
Does anyone have an idea whta is wrong with the code? I am sure it is a simple solution. Been banging my head against this screen, can't see the woods from the tree at the moment.
The problem is that when the for code block (the code enclosed in parenthesis) is parsed, all the variable read operations are replaced with the value in the variable before starting to execute, and in each iteration what is used is this initial value, and not the value stored into the variable during the execution.
If you change a variable inside a block of code and need to access the changed value inside the same block of code, you need to enable delayed expansion (setlocal enabledelayedexpansion) and change (where necessary) the syntax to access the variables to use !varName! instead of %varName%. This indicates to the parser that this read operation must be delayed.
So, in your code you have delayed expansion enabled, but
SET data.path=%data.path:~0,-1%
echo %data.path%
should be something like
SET data.path=!data.path:~0,-1!
echo !data.path!
Here's a trick to remove the trailing backslash
#echo off
set "folder=c:\data\"
for %%a in ("%folder%\.") do set "folder=%%~dpnxa"
set fold
pause

Batch file if statement weirdness with variable assignment and expansion

I have a batch file and I'm trying to work with some variables in an if statement. For example I set a variable either from command line parameter 1, or if there is no parameter 1 then I have a default value. Then, depending on whether the value is the default or an argument from the command line I perform some other function.
What I notice is that the variable I've assigned in the if statement isn't available after the if statement is over. Can someone explain why that is?
For example this will fail to echo what's in TEST1:
#echo off
setlocal ENABLEEXTENSIONS
if .%1==. (
set TEST1=NOTFOUND
echo %TEST1%
) else (
set TEST1=%1
echo %TEST1%
)
If I run that batch file with or without an argument it returns ECHO is off.. It thinks the variable is still empty.
If I attempt to get the variable after the if statement, it works:
#echo off
setlocal ENABLEEXTENSIONS
if .%1==. (
set TEST1=NOTFOUND
) else (
set TEST1=%1
)
echo %TEST1%
I'm sure this has to do with variable expansion but it really threw me. It looks like I will assign another variable in the if statement like set USING_DEFAULT_TEST1=TRUE and set USING_DEFAULT_TEST1=FALSE or compare to the default value or something.
Within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed.
Hence, IF (something) else (somethingelse) will be executed using the values of %variables% at the time the IF is encountered.
Two common ways to overcome this are 1) to use setlocal enabledelayedexpansion and use !var! in place of %var% to access the changed value of var or 2) to call a subroutine to perform further processing using the changed values.

set fileName and echo to cmd.exe in for loop of batch?

I'd like to put each of the many properties' file names into variable fileName and echo them out to the command prompt window. But only the last properties file name to be cycled thru is printed out as many times as there are properties files. Is there an easy fix to this problem. I know that ...DO echo %%-nxG can do the same thing but I'd like to save the file name in %%~nxG for future use.
FOR %%G IN (C:\ExecutionSDKTest_10.2.2\*.properties) DO (
set fileName=%%~nxG
echo %fileName%
)
You need to use delayed expansion:
setlocal enabledelayedexpansion
FOR %%G IN (C:\ExecutionSDKTest_10.2.2\*.properties) DO (
set fileName=%%~nxG
echo !fileName!
)
Environment variables in cmd are expanded when a command is parsed – in this case this includes the whole block in parentheses. So %fileName% gets replaced by an empty string because it didn't have a value before the loop ran. Delayed expansion uses ! instead of % and changes variable evaluation so that they are evaluated just before a command is run.
help set has more details about why and when it is necessary. In general, whenever you modify and use a variable within a loop you have to use delayed expansion, but it comes with a few other benefits too.

Variable assignment problem in DOS batch file for loop

I have a variable assignment problem inside the DOS script for loop. It never assigns the value, its always blank. Below the sample code
#echo off
set ans=%1
SET STRING=%ans:"=%
echo Parsing the string "%STRING%":
for /f "tokens=1-2" %%a in ("%STRING%") do (
set Word1 =%%a
echo Word 1: %%a
echo Word 1: %Word1%
set Word2 =%%b
if %%b.==. (Set server =\\.\pipe\mssql$microsoft##ssee\sql\query ) else (Set server =%%b)
)
echo Server name "%server%"
sqlcmd -s %server%
value of %%a is not assigned to variable Word1. But when i echo the %%a, it shows the correct value. As well in the last empty value check if condition, the server variable never set. I am very confused here. can someone help me out??
P.S: input to the script is any 2 word string (ex: a.bat "l dev-server")
You need to use delayed expansion - !Word1! instead of %Word1%.
By default, when the shell first reads the statement, all variables are substituted with their current values, and the modified statement is used every time that line is hit. This is simply how DOS works, and it's kept that way in the Windows shell for backwards compatibility.
Delayed expansion, on the other hand, reinserts the value every time the statement is hit. That will give you the desired result.

Resources