I have a variable "var1" with contents of "sharp=soothe"
var1=sharp=soothe
How do I create a new variable using only var1 contents to get this
sharp=soothe
I want to use the set command somehow like this
set %var1%=
but I can't get further than this. I hope I don't have to search for strings before and after the = sign. I thought there would be a faster way than that.
OK, I am so close. I found some other code I'm using and this nearly works for an infinite number of variables but the set command syntax is wrong again.
set var1=unfilter=rem
set var2=mdegrain=rem
set n=0
:parseArgs2
set /a n+=1
if defined var%n% (
set %var%n%%
shift /1
goto :parseArgs2
)
Just remove one character from your attempt :-)
set %var1%
If you have an "array" of variables: var1, var2, ... var20, then you can use a FOR /L loop with delayed expansion:
setlocal enableDelayedExpansion
rem define your variables here ...
for /l %%N in (1 1 20) do set !var%%N!
Or if you have a series of randomly named variables, a simple FOR will do:
setlocal enableDelayedExpansion
rem define your variables here ...
for %%V in (varA varB varC varD) do set !%%V!
update in response to add-on question
I don't see why you are using SHIFT, unless there is other code you haven't shown. I removed the SHIFT.
set var1=unfilter=rem
set var2=mdegrain=rem
set n=0
:parseArgs2
set /a n+=1
if defined var%n% (
call set %%var%n%%%
goto :parseArgs2
)
or
setlocal enableDelayedExpansion
set var1=unfilter=rem
set var2=mdegrain=rem
set n=0
:parseArgs2
set /a n+=1
if defined var%n% (
set !var%n%!
goto :parseArgs2
)
Try this:
set var1=sharp=soothe
for /f "tokens=*" %%a in ("%var1%") do set %%a
echo %sharp%
or just:
for %%a in ("%var1%") do set %%a
Perhaps is this what you want?
#echo off
setlocal EnableDelayedExpansion
set var1=unfilter=rem
set var2=mdegrain=rem
set var10=resize=rem
for /F "tokens=1* delims==" %%a in ('set var') do set %%b
In this method does not matter the number of defined variables nor its order; all defined variables are always processed.
EDIT: Below is the output when an ECHO command is placed before the set %%b:
set unfilter=rem
set resize=rem
set mdegrain=rem
Related
In my batch I want to copy a variable amount of source- to target destinations.
I want to define like this:
#setlocal EnableDelayedExpansion
set source1="C:\folder1"
set target1="f:\folder1"
set source2="C:\folder2"
set target2="f:\folder2"
...
set sourcen="C:\foldern"
set targetn="f:\foldern"
Dependently from a defined amount of folders
set numFolder=5
I want to go through the folders in a loop:
set /a COUNT=0
:LOOP
echo %COUNT%
set /a COUNT+=1
rem write the NAME of the parameter variable (source1,source2 etc.) in nameor
set "nameor=source%COUNT%"
rem write the VALUE of the parameter variable (source1,source2 etc.) into origin ("C:\folder1", "C:\folder2")
set "origin=%nameor%"
echo %origin%
if %COUNT% lss %numFolder% goto LOOP
When I show
echo %nameor%
I get what I expectet: source1, source2 etc.
but
echo %%%origin%%%
only provides
source1
instead of the expected value
"C:\folder1"
I thought, that I could resolve this by using DelayedExpansion but what did I miss?
To avoid confusion for me, I change the "origin" to "source". E.g. set "origin=%nameor%" changed to set "source=%nameor%".
To print out "C:\folder1" to "C:\foldern", you should use echo !%source%!, else you will just see "source1" to "sourcen".
Your problem is just about array element management. Try this:
#echo off
setlocal EnableDelayedExpansion
rem Define the two arrays
set i=0
for %%a in ("C:\folder1=f:\folder1"
"C:\folder2=f:\folder2"
"C:\foldern=f:\foldern") do (
set /A i+=1
for /F "tokens=1,2 delims==" %%b in (%%a) do (
set source!i!="%%a"
set target!i!="%%b"
)
)
rem Show up to numFolder elements of both arrays
set numFolder=5
for /L %%i in (1,1,%numFolder%) do (
echo %%i- Source%%i=!source%%i!, Target%%i=!target%%i!
)
The first part is equivalent to your series of individual element assignments. In this way is easier to add new pairs of values.
For further description on array management in Batch files, see: Arrays, linked lists and other data structures in cmd.exe (batch) script
Code:
#echo off
set /a x=5
set /a TNUM=%x%-3
echo %TNUM%
pause
And the output is:
ECHO is off
So, how do I fix this?
Here's the full code:
#echo off
setlocal enableextensions
set /a COLUMNS=60
set /a ROWS=40
mode con: cols=%COLUMNS% lines=%ROWS%
:main
set /a xpos=5
set /a ypos=5
call:displayScreen
pause >nul
goto :eof
::FUNCTIONS::
:displayScreen
set /a TEMPR=%ROWS%-3
for /l %%c in (1,1,%COLUMNS%) do echo|set /p=#
for /l %%r in (1,1,%TEMPR%) do (
echo|set /p=#
if %%r==%ypos% (
set /a TNUM=%xpos%-3
echo %TNUM%
for /l %%t in (1,1,%TNUM%) do echo|set /p=A
echo|set /p=o
set /a TNUM=%COLUMNS%-%xpos%-1
for /l %%t in (1,1,%TNUM%) do echo|set /p=A
) else (
set /a TNUM=%COLUMNS%-2
for /l %%t in (1,1,%TNUM%) do (
echo|set /p=A
)
)
echo|set /p=#
echo.
)
for /l %%c in (1,1,%COLUMNS%) do echo|set /p=#
The main problem is in the display screen function. It is supposed to print the screen and have the x at the two coordinates I gave it. I put in echo %TNUM% so I could see what was in it, and it always amounts to "".
There are mainly three ways to obtain the indicated behaviour
1 - Problems with variable expansion. In batch files, lines or blocks of lines (a set of lines enclosed in parenthesis), are parsed, executed and then the operation is repeated on the next line/block.
During the parse phase all read operations on variables (any %var%) are removed from code, replacing them with the value stored in the variable before starting to execute the line/block of code.
So, if inside the line/block the value of a variable is changed, this value can not be readed/retrieved inside the same line/block during execution phase. Why? Because there are no read operations in the line/block to retrieve the value of the variable. This code
set x=0
if 1==1 (
set x=1
set y=%x%
)
echo x=%x% y=%y%
will echo to console x=1 y=0 Why? When the if block of code is parsed (the full block), the read operations (=%x%) where replaced at parse time with the values inside the variables at parse time and the code executed is
if 1==1 (
set x=1
set y=0 <- Read operation in x replaced with its value at parse time
)
that is, the changed value stored in x can not be retrieved.
This case is usually solved with the usage of delayed expansion. It is a behaviour that can be activated with command setlocal enabledelayedexpansion and that allow the programmer to change, where needed, the syntax to access variable content from %var% to !var!, indicating to the parser that the expansion of the variable (the replacement of the read operation with the value of the variable) must be delayed until the execution phase. In the proposed sample it should be
setlocal enabledelayedexpansion <- delayed expansion enabled
set x=0
if 1==1 (
set x=1
set y=!x! <- Read operation delayed
)
echo x=%x% y=%y%
2 - Spaces. An habitual problem with variable declaration is
set /a TNUM = %x%-3
^.......This value is included in the variable name
The space is included in the variable name, and echoing %TNUM% fails as this is not the name of the variable, it is named %TNUM %.
BUT as i forgot and #jeb corrects in comments, while set and set /p exhibit this behaviour, this will NOT happen when set /a is used. In this case a parser with different rules is used (for example, it allows variables references without previous expansion) and the indicated space will be supressed.
3 - You machine has extensions disabled by default. If extensions are disabled, set /a will not work and no value will be assigned to the variable
#echo off
setlocal enableextensions
set /a x=5
set /a TNUM=x-3
echo %TNUM%
Summarizing, to execute the line echo %TNUM% and obtain the indicated ECHO is off it is necessary that when the line is parsed TNUM is not defined or it only contain spaces or tabs.
As, a priori, when the line set /a TNUM=%x%-3 is executed, a value is assigned to the variable (at least the -3), it seems your problem is the case 1 (the echoed value was replaced before the execution) or the case 3 (you have no value to echo as the set operation failed).
edited With all the code posted, the problem is delayed expansion.
#echo off
setlocal enableextensions enabledelayedexpansion
set /a COLUMNS=60
set /a ROWS=40
mode con: cols=%COLUMNS% lines=%ROWS%
:main
set /a xpos=5
set /a ypos=5
call :displayScreen
pause >nul
goto :eof
::FUNCTIONS::
:displayScreen
set /a TEMPR=%ROWS%-3
for /l %%c in (1,1,%COLUMNS%) do echo|set /p=#
for /l %%r in (1,1,%TEMPR%) do (
echo|set /p=#
if %%r==%ypos% (
set /a TNUM=%xpos%-3
for /l %%t in (1,1,!TNUM!) do echo|set /p=A
echo|set /p=o
set /a TNUM=%COLUMNS%-%xpos%
for /l %%t in (1,1,!TNUM!) do echo|set /p=A
) else (
set /a TNUM=%COLUMNS%-2
for /l %%t in (1,1,!TNUM!) do (
echo|set /p=A
)
)
echo|set /p=#
)
for /l %%c in (1,1,%COLUMNS%) do echo|set /p=#
It looks like it isn't waiting to evaluate %x%. So you can add ENABLEDELAYEDEXPANSION so that it will wait to evaluate x so that it will be initialized during the math operation.
#echo off
setlocal ENABLEDELAYEDEXPANSION
set /a x=5
set /a TNUM=!x!-3
echo !TNUM!
endlocal
pause
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.
Since the for...%%r... loop executes a block of code (from the ( following the do through to the matching ) ) then any %tnum% appearing within that block will be replaced by the value of tnum when the loop was parsed. Tnum has no value when the block is started, so that's what is shown.
tnum is being set within the loop, so since you have enabledelayedexpansion set to ON in the setlocal enabledelayedexpansion statement at the start of the program, you need to use !tnum! in place of %tnum% within the loop to access the value of tnum as it changes.
:LoadSelect1
set /p Name=Enter character to load:
cls
if EXIST "%Name%_Savefile.txt" goto Load
goto LoadError
:Load
for /f "tokens=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26" %%a in (%Name%_Savefile.txt) do call :Process %%a %%b %%c %%d %%e %%f %%g %%h %%i %%j %%k %%l %%m %%n %%o %%p %%q %%r %%s %%t %%u %%v %%w %%x %%y %%z
goto Stats
:Process
set Location=%1
set Name=%2
set Gender=%3
set Age=%4
set Gold=%5
set Hunger=%6
set Illness=%7
set Wounds=%8
set CHP=%9
set MHP=%10
set CMP=%11
set MMP=%12
set DMG=%13
set DFN=%14
set INT=%15
set DEX=%16
set STR=%17
set Head=%18
set Shoulder=%19
set Neck=%20
set Chest=%21
set Glove=%22
set Leg=%23
set Feet=%24
set LTWP=%25
set RTWP=%26
Above code is used to retrieve variables saved to a .txt file in my batch script. Variables save fine to individual lines. When Call :Process function is used, %1 is applied for %1, and "%1X" become %1 with number attached. Meanwhile %2-9 are blank, and %2X are blanks with number attached. Solution would be great.
Pastebin of full code
To recreate: Run as batch file. Go through prompts until section with 9 choices. Type "Save". Close and reopen. Go to Load option. Type in name from previous. View errors.
It would be easier if you save your savefile.txt in this format :
savefile.txt
Location=AAAA
Name=BBBB
Gender=CCCC
Age=DDDD
Gold=EEEE
Hunger=FFFF
Illness=GGGG
Wounds=HHHH
and then to set the variables saved in savefile.txt :
for /f "delims=" %%a in (savefile.txt) do set %%a
You can also save your savefile as .bat then it will look like this :
set Location=AAAA
set Name=BBBB
----
and with the call:savefile.bat all your Vars will be evaluated
I was recently given the advice to export variables from a batch script to a text file using
set $>savefile
and returning said variables upon another startup using
for /f "delims=" %%a in (savefile) do set %%a
In implementation, this is not creating a file with the variables, resulting in error in the script, which closes the batch. To remedy the closing, I created a check to display an error message, but still cannot get the script to export variables into a file.
Variables
::Variables
set Location=Und
set Name=Und
set Gender=Und
set Age=Und
set Gold=0
set Hunger=Satisfied
set Illness=None
set Wounds=None
set CHP=10
set MHP=10
set CMP=0
set MMP=0
set DMG=(%STR%/5)+%RTWPDMG%
set DFN=0+%HeadAR%+%NeckAR%+%Shoulder%+%Chest%+%Glove%+%Leg%+%Feet%
set INT=1
set DEX=1
set STR=1
set Head=----
set Shoulder=----
set Neck=----
set Chest=Shirt
set Glove=----
set Leg=Pants
set Feet=Shoes
set LTWP=----
set RTWP=----
goto Start
Save script
::Save
:Save
cls
set $Location=%Location%
set $Name=%Name%
set $Gender=%Gender%
set $Age=%Age%
set $Gold=%Gold%
set $Hunger=%Hunger%
set $Illness=%Illness%
set $Wounds=%Wounds%
set $CHP=%CHP%
set $MHP=%MHP%
set $CMP=%CMP%
set $MMP=%MMP%
set $DMG=%DMG%
set $DFN=%DFN%
set $INT=%INT%
set $DEX=%DEX%
set $STR=%STR%
set $Head=%Head%
set $Shoulder=%Shoulder%
set $Neck=%Neck%
set $Chest=%Chest%
set $Glove=%Glove%
set $Leg=%Leg%
set $Feet=%Feet%
set $LTWP=%LTWP%
set $RTWP=%RTWP%
set $>%Name%_Savefile.txt
cls
echo SAVED
echo.
echo.
pause
Goto Start
And the load script
::Select Character to Load
:LoadError
Echo INVALID NAME
echo.
echo.
echo.
echo.
goto LoadSelect1
:LoadSelect
cls
goto LoadSelect1
:LoadSelect1
set /p Name=Enter character to load:
goto Load
:Load
cls
if EXIST %Name%_Savefile.txt goto LoadSuccess
goto LoadError
:Load
for /f "delims=" %%a in %Name%_Savefile.txt do set %%a
goto Stats
Am I missing something? I'd like all variables beginning with $ to be targeted without needing to list each one.
Bonus Question:
If I use $Variable for the original variables, are they still called using %Variable%? If so, it'd cut down quite a bit of clutter.
--I realize batch is a sub-optimal language to script in, but it is the only one I am familiar with. I am currently working on lua and C++, and plan on practicing the two by converting the batch file later.
EDIT: The file is created, but only contains $ in line 1 and nothing else. I assume I have misunderstood the use of the variable export command.
It is quite simple say if you had %test% as your variable. (But it can be %% any thing) You should then do this:
echo %test%
echo %test% >nameofyourtextfile.txt
Then to read the you should use:
for /f "delims=" %%x in (nameofyourtextfile.txt) do set "test=%%x"
echo %test%
works for the first line of the text file and >> sends the variable to last line. So you could use these like so:
echo %test1% > nameofyourtextfile.txt
echo %test2% >> nameofyourtextfile.txt
echo %test3% >> nameofyourtextfile.txt
To load from different lines you would use:
for /F "tokens=1,2,3" %%i in (nameofyourtextfile.txt) do call :process %%i %%j %%k
goto thenextstep
:process
set test1=%1
set test2=%2
set test3=%3
You could add more tokens like so:
for /F "tokens=1,2,3,4,5" %%i in (nameofyourtextfile.txt) do call :process %%i %%j %%k
goto thenextstep
:process
set test1=%1
set test2=%2
set test3=%3
set test4=%4
set test5=%5
If you do it the way you posted, what you have is (just a extract)
in your main code set Wounds=None
in your save code set $Wounds=%Wounds% set $>filename
in your load code for %%a .... set %%a that gets translated to set $Wounds=None
At the end, you have not assigned a loaded value to %Wounds%, so an aditional set Wounds=%$Wounds% is needed.
I think the advice on using $ you received was an advice on using a common prefix to all the variables you want to save, so, you can send them and load them from a file with little effort. You can use $ or whatever other thing you want as long as it is a valid sintax and you change all the variables that you want save/load to that sintax.
By example
call :initializeCharacter
....
call :save
....
call :load
:initializeCharacter
set "ch.Name=Und"
set "ch.Gender=Und"
....
exit /b
:save
set ch.>"c:\savedGames\save.txt"
exit /b
:load
for /f "usebackq delims=" %%a in ("c:\savedGames\save.txt") do set "%%a"
exit /b
When the load function gets called, the variables (named ch.whatever and saved as ch.whatever) get overwritten on file read. That way, the variables in your code, what you save and what you load, all, have the same names. No need to set $myVar=%myVar% on save or set myVar=%$myVar%on load
And use $, ch., .... or whatever prefix you prefer in all the variables that needs to be saved/loaded in a block. What you don't want to save/load should have names that do not match the prefix used in the set prefix>file. Better option is to use different prefixes for different domain/level/conceptual element/...
How can I increment part of a variable name?
EG. i want to increment
filename holds something like mmddyyyy-prev01-Database.mdb
and I'm trying to rename it to mmddyyyy-prev02-Database.mdb
With the code
Setlocal EnableDelayedExpansion
set /a nextFileName=!fileName:~0,13!!fileName:~13!+1
and I get the error
Missing operator.
I am using this to make a batch sub that will recursively rename the prevXX items so that the newest one is always prev01. I'm also doing regular setlocal in my method to keep the variables from getting mixed up between the deeper calls
I'm going the hard way:
#ECHO OFF &SETLOCAL disableDelayedExpansion
FOR /f "delims=" %%x IN ('DIR /b /a-d /o-n *-prev*-Database.mdb') DO (
FOR /f "tokens=1-3*delims=v-" %%a IN ("%%~x") DO (
FOR /f "tokens=*delims=0" %%e IN ("0%%c") DO (
FOR /f %%f IN ('SET /a %%e+1') DO (
IF %%f LEQ 9 (
ECHO REN "%%~x" "%%~a-%%~bv0%%~f-%%~d"
) ELSE (
ECHO REN "%%~x" "%%~a-%%~bv%%~f-%%~d"
)
)
)
)
)
Just remove the ECHOs to make it working.
SET /A won't allow you do this.
You'll have to break up into two lines, like this:
SET BaseFileName=!FileName:~0,13!
SET /A Counter=!FileName:~13!+1
Then, put the pieces back together:
SET NextFileName=%BaseFileName%%Counter%-Database.mdb
NOTE: This will not handle your leading 0...you'll have to write more code for that.
...
set filename=mmddyyyy-prev01-Database.mdb
call :nextname
ren %filename% %newname%
...
:nextname
set /a newname=1%filename:~13,2%+1
set newname=%filename:~0,13%%newname:~-2%%filename:~15%
goto :eof
Whether you'd use ren %filename% %newname% or ren !filename! !newname! is a matter of context. You've posted insufficient code to be certain. If, as I suspect, you are executing the ren in a loop, then you would need the !var! form and delayedexpansion (with which you appear familiar.) Within the subroutine :nextname, the %var% form should be used.
but remember that you need to rename your existing files in reverse-alphabetical order, so
mmddyyyy-prev03-Database.mdb becomes mmddyyyy-prev04-Database.mdb
mmddyyyy-prev02-Database.mdb becomes mmddyyyy-prev03-Database.mdb
mmddyyyy-prev01-Database.mdb becomes mmddyyyy-prev02-Database.mdb