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
Related
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
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%
I'm looping through all command-line arguments using SHIFT. I'm getting result of ECHO is off.. It is likely printing the empty variable.
:argLoopStart
SET paramName=
SET arg=%1
IF -%arg%-==-- GOTO argLoopEnd
IF %arg:~0,2%==-- (
SET paramName=%arg%
ECHO %arg%
ECHO %paramName%
)
SHIFT
GOTO argLoopStart
:argLoopEnd
By running the command fake-command --dbs=mydbname, I got this:
--dbs
ECHO is off.
According to the code above, ECHO %arg% prints --dbs and ECHO %paramName% prints ECHO is off. The line of SET paramName=%arg% is not working as I expected. %parameName% should print --dbs as well. However, it seems printing an empty variable.
You need to enable delayed expansion with SETLOCAL EnableDelayedExpansion at the top of your script:
Delayed Expansion will cause variables to be expanded at execution
time rather than at parse time, this option is turned on with the
SETLOCAL command. When delayed expansion is in effect variables may be
referenced using !variable_name! (in addition to the normal
%variable_name% )
#echo off
setlocal enabledelayedexpansion
:argLoopStart
set paramName=
set arg=%1
if -!arg!-==-- goto argLoopEnd
if %arg:~0,2%==-- (
set paramName=!arg!
echo !arg!
echo !paramName!
)
shift
goto argLoopStart
:argLoopEnd
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%:
I want to store a URL prefix in an Windows environment variable. The ampersands in the query string makes this troublesome though.
For example: I have a URL prefix of http://example.com?foo=1&bar= and want to create a full URL by providing a value for the bar parameter. I then want to launch that URL using the "start" command.
Adding quotes around the value for the SET operation is easy enough:
set myvar="http://example.com?foo=1&bar="
Windows includes the quotes in the actual value though (thanks Windows!):
echo %myvar%
"http://example.com?foo=1&bar=true"
I know that I can strip quotes away from batch file arguments by using tilde:
echo %~1
However, I can't seem to do it to named variables:
echo %~myvar%
%~myvar%
What's the syntax for accomplishing this?
echo %myvar:"=%
This is not a limitation of the environment variable, but rather the command shell.
Enclose the entire assignment in quotes:
set "myvar=http://example.com?foo=1&bar="
Though if you try to echo this, it will complain as the shell will see a break in there.
You can echo it by enclosing the var name in quotes:
echo "%myvar%"
Or better, just use the set command to view the contents:
set myvar
While there are several good answers already, another way to remove quotes is to use a simple subroutine:
:unquote
set %1=%~2
goto :EOF
Here's a complete usage example:
#echo off
setlocal ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS
set words="Two words"
call :unquote words %words%
echo %words%
set quoted="Now is the time"
call :unquote unquoted %quoted%
echo %unquoted%
set word=NoQuoteTest
call :unquote word %word%
echo %word%
goto :EOF
:unquote
set %1=%~2
goto :EOF
This works
for %a in (%myvar%) do set myvar=%~a
I would also use this if I wanted to print a variable that contained and ampersand without the quotes.
for %a in ("fish & chips") do echo %~a
To remove only beginning and ending quotes from a variable:
SET myvar=###%myvar%###
SET myvar=%myvar:"###=%
SET myvar=%myvar:###"=%
SET myvar=%myvar:###=%
This assumes you don't have a ###" or "### inside your value, and does not work if the variable is NULL.
Credit goes to http://ss64.com/nt/syntax-esc.html for this method.
Use delayed environment variable expansion and use !var:~1,-1! to remove the quotes:
#echo off
setlocal enabledelayedexpansion
set myvar="http://example.com?foo=1&bar="
set myvarWithoutQuotes=!myvar:~1,-1!
echo !myvarWithoutQuotes!
Use multiple variables to do it:
set myvar="http://example.com?foo=1&bar="
set bar=true
set launch=%testvar:,-1%%bar%"
start iexplore %launch%
#echo off
set "myvar=http://example.com?foo=1&bar="
setlocal EnableDelayedExpansion
echo !myvar!
This is because the variable contains special shell characters.
I think this should do it:
for /f "tokens=*" %i in (%myvar%) do set %myvar%=%~i
But you do not need this,
set myvar="http://example.com?foo=1&bar="
start "" %myvar%
Will work too, you just need to supply a title to the start command.