How to Reassess Batch variables? - windows

Is it possible to reassess Batch variables?
Here an example what i want to do :
set a=Hello
set b=%a%
set a=Bye
echo %b%
rem Here, i want to show 'Bye' instead of 'Hello'

You could use delayed expansion to pass by reference.
setlocal enabledelayedexpansion
set "a=Hello"
set "b=a"
set "a=Bye"
echo !%b%!
... should output Bye. You're setting b to the variable name of a with this method. In the echo line, the batch thread first gets the value of b, which is a. It then expands the value of !a! via delayed expansion.
Be advised that if your values are likely to contain exclamation marks, you probably need to limit enabledelayedexpansion to the retrieval of your values only, avoiding having it active during the variable setting.
set "a=Hello"
set "b=a"
set "a=Bye"
setlocal enabledelayedexpansion
echo !%b%!
endlocal

You may store in variable b a reference to the value in a, and then use Delayed Expansion to access it:
#echo off
setlocal DisableDelayedExpansion
rem Store in "b" a *reference* to the value in "a"
set b=!a!
setlocal EnableDelayedExpansion
set a=Hello
echo Show the value in a: %b%
set a=Bye
echo Show the value in a: %b%

Related

How to put a Variable inside a variable in batch

I'm making a bat file which add/load/rename savegame slots, and I had problem with some variables, the first variable is savegame number, the second variable is the name of the savegame, here is the code I've tried:
#echo off
set savename2=FortySeven
set nb=2
echo %savename%nb%%
pause
The result I have got is nb%
Either enable delayed expansion:
#Echo Off
Set "savename2=FortySeven"
Set "nb=2"
SetLocal EnableDelayedExpansion
Echo !savename%nb%!
Pause
EndLocal
Or, use Call for that expansion:
#Echo Off
Set "savename2=FortySeven"
Set "nb=2"
Call Echo %%savename%nb%%%
Pause

Removing leading/trailing slashes in .BAT file ruins everything

I'm having a lot of trouble figuring out why my .bat file randomly closes... but I've narrowed it down to this:
IF %var:~-1%==\ SET var=%var:~0,-1%
IF %var:~0,1%==\ SET var=%var:~1%
Ideally what I'm eventually trying to accomplish is to remove any leading or trailing slashes or backslashes from %var%, which is set by user input.
The above lines work, but not when inside an IF statement when setlocal EnableDelayedExpansion is set AND when the variable is also set inside the IF statement.
For example, the following works if I remove the above lines, but crashes otherwise:
#echo off
setlocal EnableDelayedExpansion
set somevar=blah
if !somevar!==blah (
set /p var="-> "
IF %var:~-1%==\ SET var=%var:~0,-1%
IF %var:~0,1%==\ SET var=%var:~1%
echo !var!
)
pause
If I remove the IF condition, however, it does work:
#echo off
setlocal EnableDelayedExpansion
set /p var="-> "
IF %var:~-1%==\ SET var=%var:~0,-1%
IF %var:~0,1%==\ SET var=%var:~1%
echo !var!
pause
And if I keep the IF statement but move the set /p var="-> " outside of it, then it also works:
#echo off
setlocal EnableDelayedExpansion
set somevar=blah
set /p var="-> "
if !somevar!==blah (
IF %var:~-1%==\ SET var=%var:~0,-1%
IF %var:~0,1%==\ SET var=%var:~1%
echo !var!
)
pause
I know that when Delayed Expansion is enabled that I have to use ! instead of % for variables, but I've tried every combination of that on those lines to no avail, and I don't understand how they work enough to troubleshoot further.
And if there's a more efficient way to accomplish what I've described, by all means please mention it. I'm somewhat new to batch-writing.
As you've implied, the key to your problem is the use of delayed expansion.
When delayedexpansion has been invoked,
%var% refers to the value of var as it was when any code block (parenthesised sequence of instructions) was encountered (actually, var is simply replaced by its value at that time and then the instructions are executed)
!var! refers to the run-time value of var - that is, the value at the current time, having possibly been altered by the operation of the code block.
so, examining from your code
set /p var="-> "
IF %var:~-1%==\ SET var=%var:~0,-1%
IF %var:~0,1%==\ SET var=%var:~1%
echo !var!
This is executed as
set /p var="-> "
IF ==\ SET var=
IF ==\ SET var=
echo !var!
because the value of var at the start of the code is nothing, consequently it "crashes" because the if statement syntax is incorrect.
To fix the problem, use
set /p var="-> "
IF !var:~-1!==\ SET var=!var:~0,-1!
IF !var:~0,1!==\ SET var=!var:~1!
echo !var!
where each reference to var becomes the value as changed by the "set /p" or subsequent set operations.
However, set/p does not clear the variable if you simply press Enter. It leaves the variable unchanged (but its value happens to be nothing at the time)
Hence, a better approach is
set /p var="-> "
IF "!var:~-1!"=="\" SET "var=!var:~0,-1!"
IF "!var:~0,1!"=="\" SET "var=!var:~1!"
echo !var!
where the quotes around the if arguments ensure that the arguments are not empty and those around the set arguments ensure that the variable does not acquire any stray trailing spaces that may exist on the instruction line.

windows batch command nested variable

I have a requirement to use nested variables for creating a folder based on a environment variables.
Assume I have variables listed below:
%ABC_ASIA_LOCATION%
%ABC_EUROPE_LOCATION%
%ABC_US_LOCATION%
and I want to pass the country as variable like %ABC_%COUNTRY%_LOCATION%.
How do I achieve this in Windows utilizing batch scripting?
you have to enclose each variable into %:
set "ABC=ABC"
set "COUNTRY=EUROPE
set "LOCATION=MUNICH
echo %ABC%_%COUNTRY%_%LOCATION%
Result: ABC_EUROPE_MUNICH
Or if you just want Country as a variable, keeping the rest fixed:
echo ABC_%COUNTRY%_LOCATION
Result: ABC_EUROPE_LOCATION
or if you want the whole thing to be a variable (a variable name containing another variable), you have to use delayed expansion:
setlocal enabledelayedexpansion
set country=EUROPE
set "ABC_EUROPE_LOCATION=a town in southern Germany"
echo !ABC_%country%_LOCATION!
which gives you: a town in southern Germany
Note: setlocal has no effect outside of batchfiles, so delayed expansion works only:
- in batchfiles
- when the command prompt was started with delayed expansion enabled (cmd /v:on) (by default, the command prompt runs with delayed expansion disabled)
There are times when you need the nested variable to work inside a for loop, which already requires the !varname! syntax for the variable expansion. When this is the case, !ABC_%country%LOCATION!, will not work (reference Stephan's post on 9/6/2017 at 7:24). Neither will !ABC!country!_LOCATION!.
The following batch file demonstrates this. This is a somewhat contrived example to demonstrate the issue. In the subroutine, we can also set a variable to the nested value if you didn't want to do the work in the subroutine.
#echo off
setlocal enabledelayedexpansion
setlocal
set var1=value1
set var2=value2
set var3=value3
set var4=value4
set var5=value5
for %%A in (var1 var2 var3 var4 var5) do (
set varname=%%A
echo 1.This will not work: !varname!=!!varname!!
echo 2.This will not work: !varname!=%!varname!%
echo 3.This will not work: !varname!=!%varname%!
echo 4.This will not work: %varname%=%varname%
echo 5.This will not work: %varname%=%%varname%%
call:NestedVar %%A
call:getNestedVar new%%A %%A
echo new%%A=!new%%A!
echo.
echo.
)
goto:eof
:NestedVar
echo This will work: %1=!%1! (but only if setlocal enabledelayedexpansion is used)
goto:eof
:getNestedVar
REM Use: getNestedVar newVariableName varName
REM Will set newVariableName to the value of varName
echo Setting variable, %1=!%2!
set %1=!%2!
goto:eof

SET command expantion substrings

When using the SET command in command prompt, what does % and ! mean, for example
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" #echo If you see this, it worked)
set LIST=
for %i in (*) do set LIST=!LIST! %i
echo %LIST%
Notice how there's %i and !VAR! what does this mean, %i cant be a variable right? as variables are written out like %variable%.
Any ideas what these are?. Also is the (*) just a literal?
Regards, S
In normal cases you can access variable value by enclosing its name with % :
set var=a
echo %var%
As when there is composition of commands with & or a few commands are put in brackets the set will take effect after all of them are executed.Then you need delayed expansion to access your variable and to enclose it with !
setlocal enableDelayedExpansion
if exist c:\ (
set var2=a
echo !var2!
)
Take a more close look at SET command.
in for loops you have a special variables that works only in context of for command - a tokens that change its values on each iteration of the loop:
for /f "delims=" %%# in ('set') do echo %%#
here are the letters that you can use as tokens.And be careful - you need to use double % in a script and a single in command prompt.
There's one more type of variables that has a % only at the beginning - arguments passed to the script (or a subroutine) - accessible with numbers (or rather digits) - from %0 to %9 where %0
is the name of the script itself.

How to survive "delayed variable expansion" in a Windows batch script

This is my script:
#echo off
setlocal
for /f %%i in ('echo aaa/') do set REPO=%%i
if "%REPO%"=="" (
echo No input
) else (
echo %REPO:~-1%
echo %REPO:~0,-1%
if %REPO:~-1%==/ set REPO=%REPO:~0,-1%
echo %REPO%
)
endlocal
Please, observe:
c:\dev\shunra\GlobalLibrary\Server>c:\Utils\hgbackup.cmd
/
aaa
aaa/
c:\dev\shunra\GlobalLibrary\Server>
What is going on?
EDIT
Note, that I am assigning to REPO something that evaluates to "aaa", hence I expect it to print "aaa", not "aaa/". It drives me crazy.
EDIT2
Apparently, here is the culprit (from help on the set command):
Finally, support for delayed environment variable expansion has been
added. This support is always disabled by default, but may be
enabled/disabled via the /V command line switch to CMD.EXE. See CMD /?
Delayed environment variable expansion is useful for getting around
the limitations of the current expansion which happens when a line
of text is read, not when it is executed. The following example
demonstrates the problem with immediate variable expansion:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" #echo If you see this, it worked
)
would never display the message, since the %VAR% in BOTH IF statements
is substituted when the first IF statement is read, since it logically
includes the body of the IF, which is a compound statement. So the
IF inside the compound statement is really comparing "before" with
"after" which will never be equal. Similarly, the following example
will not work as expected:
set LIST=
for %i in (*) do set LIST=%LIST% %i
echo %LIST%
in that it will NOT build up a list of files in the current directory,
but instead will just set the LIST variable to the last file found.
Again, this is because the %LIST% is expanded just once when the
FOR statement is read, and at that time the LIST variable is empty.
So the actual FOR loop we are executing is:
for %i in (*) do set LIST= %i
which just keeps setting LIST to the last file found.
Delayed environment variable expansion allows you to use a different
character (the exclamation mark) to expand environment variables at
execution time. If delayed variable expansion is enabled, the above
examples could be written as follows to work as intended:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" #echo If you see this, it worked
)
set LIST=
for %i in (*) do set LIST=!LIST! %i
echo %LIST%
But I tried using the ! sign, still it does not work for me. I use get ! printed on the screen or the wrong result again.
As has been discussed in the comments, and in your edited question, you need delayed expansion.
Delayed expansion must be enabled before you can use it. Within a batch script you can use setlocal enableDelayedExpansion
#echo off
setlocal enableDelayedExpansion
for /f %%i in ('echo aaa/') do set REPO=%%i
if "%REPO%"=="" (
echo No input
) else (
echo %REPO:~-1%
echo %REPO:~0,-1%
if %REPO:~-1%==/ set REPO=%REPO:~0,-1%
echo !REPO!
)
endlocal
EDIT
The above fails if the IN() clause is changed such that REPO is undefined. For example: in (echo.)
It fails because the entire IF/ELSE construct must have valid syntax, even it the ELSE clause will not be executed.
If REPO is undefined, then
if %REPO:~-1%==/ set REPO=%REPO:~0,-1%
expands to
if ~-1REPO:~0,-1
which is invalid syntax.
The problem again is solved by using delayed expansion.
#echo off
setlocal enableDelayedExpansion
for /f %%i in ('echo.') do set REPO=%%i
if "%REPO%"=="" (
echo No input
) else (
echo %REPO:~-1%
echo %REPO:~0,-1%
if !REPO:~-1!==/ set REPO=%REPO:~0,-1%
echo !REPO!
)
endlocal
Note, that I am assigning to REPO something that evaluates to "aaa"
Actually, you're conditionally assigning something. Have you testing whether the then-part is actually executing (for example, echo If Entered).
This works for me (just an extract from my whole script)
choice /C 1234567H /M "Select an option or ctrl+C to cancel"
set _dpi=%ERRORLEVEL%
if "%_dpi%" == "8" call :helpme && goto menu
for /F "tokens=%_dpi%,*" %%1 in ("032 060 064 096 0C8 0FA 12C") do set _dpi=%%1
echo _dpi:%_dpi%:

Resources