Batch map and lookup with expanded variables - batch-file

I'm trying to do this, but my "v" variable is !expanded!. Augh. I've tried flipping things around but I'm not very good with expansion. How could I adjust the map and lookup to work with an expanded variable?

#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
set map=mon;Monday;tue;Tuesday;wed;Wednesday;thu;Thursday;fri;Friday;sat;Saturday;sun;Sunday
FOR /f "delims=" %%i IN (q20764599.txt) DO (
SET "v=%%i"
CALL :setv
ECHO ==!v!==
)
ECHO +%v%+
GOTO :EOF
:setv
CALL SET v=%%map:*%v%;=%%
SET v=%v:;=&rem.%
GOTO :eof
This should simulate your wanting to work with !v!. The file q20764599.txt could contain say a single line reading tue which gets assigned to v and the magic proceeds from there...

Related

Batch: using upper for loop parameter in lower one

The command im trying to run is:
::%items% is defined elsewhere and is the amount of items per line in the file
FOR /F "usebackq tokens=1-%items% delims=," %%1 IN (`TYPE %TextFile%`) DO (
FOR /l %%a in (%items%,-1,1) do (
set /a "number=%%a"
echo !number!
:: This is the main command I believe im having issues with
set word!number!=%%!number!
echo !word1!
echo !word2!
echo !word3!
)
set /a "lineused%randomline%=1"
goto exitloop
)
:exitloop
pause
Now what I'm trying to do is set the variable called wordX where X is the number of the token. Edit: Basically, trying to use the %% variabla from the upper for loop which the lower one is running inside of.
I could type all the lines of
set word1=%%1
set word2=%%2
set word3=%%3
but that would defeat the purpose of the versatile system I'm trying to build.
Format of the text file (%TextFile%) would simply be, in this case:
line1i1,line1i2
line2i1,line2i2
But I need for it to work also on for example:
line1i1,line1i2,line1i3,line1i4
line2i1,line2i2,line2i3,line1i4
Interesting idea, but that cannot work because FOR variable expansion takes place before delayed expansion. You need a method to get an extra round of FOR variable expansion.
You can CALL a subroutine, and then use a dummy FOR loop to re-establish a FOR context. FOR variables are global in scope as long as you are in a FOR loop context. So your subroutine can access a FOR variable that was defined earlier.
...
...
FOR /F "tokens=1-%items% delims=," %%1 IN ('TYPE %TextFile%') DO (
FOR /l %%a in (%items%,-1,1) do call :set %%a
echo !word1!
echo !word2!
echo !word3!
)
...
...
exit /b
:set
for %%. in (.) do set "word%1=%%%1"
exit /b
The above works, but I don't like it because CALLs are expensive (slow). This is typically not a problem when you only have a few CALLs. But in this case the CALL is in a tight loop - one for every column times the number of rows in the file. Ouch!
If you really want to parametize your SET statements, and you want decent performance, then you can define a dynamic "macro". Simply store the needed commands in a variable, and then execute the content of the variable within your loop.
Also note that the above is limited to 9 items (10 if you start with 0 instead of 1). It is easy to extend the supported item count to 26 if you use letters, and a lookup string.
Finally, your dynamic FOR is within some parenthesized block. Presumably your ITEMS is defined outside the block, otherwise %items% could not be used in the FOR /F definition. The SET macro must be expanded using regular expansion, so it should be defined at the same time ITEMS is defined - outside the outer loop.
set /a items=3
::Define SET macro
set "v= ABCDEFGHIJKLMNOPQRSTUVWXYZ"
set "set="
for /l %%N in (1 1 %items%) do set "set=!set!&set "word%%N=%%!v:~%%N,1!""
set "set=!set:~1!"
FOR ... some loop ... DO (
...
...
FOR /F "tokens=1-%items% delims=," %%A IN ('TYPE %TextFile%') DO (
%set%
echo !word1!
echo !word2!
echo !word3!
)
...
...
)
If the ITEMS variable must be set within the outer loop, then you must CALL out of the loop to establish the inner FOR /F loop.
Note that you dont need the TYPE to get contents of file, the FOR command itself can iterate over file content
You could try something like this:
#echo off
setlocal enabledelayedexpansion
set "TextFile=textfile.txt"
set /a "lineNum=0"
set /a "i=0"
for /f "tokens=*" %%a in (%TextFile%) do (
set /a lineNum=!line_num! + 1
set "line=%%a"
for %%b in ("!line:,=" "!") do (
set /a "i=!i!+1"
set /a "wordNum=!lineNum! * !i!
set "word!wordNum!=%%b"
)
)
echo !word1!
echo !word2!
echo !word3!
Given a file with the contents:
aaa,bbb,ccc,ddd
eee,fff,ggg,hhh
Output will be:
"aaa"
"bbb"
... and so on
To remove the quotes, use %%~b at the inner FOR at set "word!wordNum! line.
Also note that you don't even need to define the number of items per line!
Hope it helps,
Cheers!

How to use variables set in a sub called in a FOR loop, with delayed expansion

This code loops through the contents of "Fire" folder on PC, which contains several subfolders. For each subfolder it runs another FOR to look for a match to directories on a tablet. If no match found it calls subroutine :missing to set variables about the missing directory.
When I get back to main routine I'm unable to echo the vars set in the sub. I've tried many permutations of !name[%CNT%]! (doubling/tripling the % and !) but can't get it right. Hope someone can lend a hand.
#Echo off
SETLOCAL EnableDelayedExpansion
:main
Set /A CNT=0
:: Fire folder contains dirs that may have been deleted from tablet.
For /F %%G IN ('dir /b %Fire%') DO (
SET thisdir=%%G
SET /A CNT+=1
:: set command that will look for %%G on the tablet
set cmd="adb shell ls /system/priv-app/!thisdir!"
:: This loop returns nul if dir exists on device
For /F "tokens=1* delims=:" %%g in ('!cmd!') do (set _N=%%h)
If NOT "!_N!"=="" call :missing !thisdir!
:: HERE'S THE PROBLEM: With this syntax I get "CNT" "CNT"
Echo AFTER THE CALL: "!name[!CNT!]!" "!pkg[!CNT!]!"
:: this displays: "pkg[3]]
Echo AFTER THE CALL: "%!name[!CNT!]%!" [!pkg[!CNT!]!]
)
:missing
:: Both %CNT% and !CNT! work here
Set pkg[%CNT%]=%1
:: set friendly name for this dir
If "%1"=="com.name.video" set "name[%CNT%]=Video"
If "%1"=="com.name.games" set "name[%CNT%]=Games"
. . .
:: THIS WORKS HERE IN THE SUB. HOW TO DISPLAY IN MAIN?
Echo MISSING: !name[%CNT%]! (!pkg[%CNT%]!)
:: Output: MISSING: Video (com.name.video)
exit /b
To display in main loop:
call Echo AFTER THE CALL: "%%name[!CNT!]%%" [%%pkg[!CNT!]%%]
Iassume you know not to use ::comments in a block and that your maun will run into your sub...
In your first AFTER THE CALL line, your script will try to evaluate !name[! and then a string literal CNT and then !]! and so on. Since both !name[! and !]! are undefined, they evaluate to nothing and you're left with the string literal, CNT. Instead, you should convert the inner !CNT! to percent notation, so it can peacefully coexist with the outer delayed variable. You can accomplish this with a for loop.
for %%I in ("!CNT!") do echo !name[%%~I]!
... to avoid the conflicting exclamation marks. In your second AFTER THE CALL line, you attempt to begin a variable with %! while also ending it with %!. When using that sort of syntax, the ! has to be outside, and the % inside (or possibly vice-versa -- see Magoo's answer for an example). But since it's inside a parenthetical code block, it won't work like you want anyway. You need the delayed expansions. Use the for loop I described above.
By the way, don't use those :: pseudo-labels as comments within a parenthetical code block. Use rem instead. The :: can break things.
Maybe you can try this:
#echo off&SetLocal EnableDelayEdexpansion
set "name[1]=something"
set "a=1"
echo !name[%a%]!
rem echo %name[!a!]%
call echo !name[%a%]!
call echo %%name[!a!]%%
call echo %%name[%a%]%%
for %%a in (!a!) do (echo !name[%%a]!)
pause

batch %var:*\% to eliminate all before last \ not first

set var=C:\Users\user\Desktop\bla\bla.exe
set var=%var:*\%
echo %var%
this returns Users\user\Desktop\bla\bla.exe - is there any way to make it focus on the last \ and not the first one so that it would just return bla.exe? bear in mind that this will be used on multiple files and folders so i won't always know how many sub-folders there are.
#ECHO OFF
SETLOCAL
set "var=C:\Users\user\Desktop\blah blah\bla.exe"
FOR %%a IN ("%var%") DO (
SET "filename=%%~nxa"
FOR %%b in ("%%~dpa.") DO SET "lastleaf=%%~nxb"
)
ECHO filename is "%filename%"
ECHO lastleaf is "%lastleaf%"
GOTO :EOF
Normally, the next question is about how to obtain the last leaf of the directory-tree. No subroutines required...
Note positioning of quotes to minimise problems with separators. ALso minor directory name-change to exhibit differences.
The following snippet shows one way to do it.
#setlocal enableextensions enabledelayedexpansion
#echo off
:main
set var=C:\Users\user\Desktop\blah blah\yada yada.exe
call :basename result "%var%"
echo %result%
endlocal
goto :eof
:basename
set %1=%~nx2
goto :eof
It basically calls a function basename (named after the UNIX utility), passing the full name and the variable you want to assign the base name to, and you need to make sure you quote it properly lest filenames containing spaces will cause you problems.
The full set of variable modifiers can be seen in the call /? help output.
Alternatively, you can use the same basename functionality in a one-liner for statement:
for /f "delims=" %%I in ("%var%") do set result=%%~nxI
This allows you to get the base name without having to call a function. I tend to prefer the function myself since it's more readable but you could probably alleviate that by just including a comment:
rem Get base name of var into result:
rem eg: var = C:\Users\user\Desktop\blah blah\yada yada.exe
rem result = yada yada.exe
for /f "delims=" %%I in ("%var%") do set result=%%~nxI

Batch Loop menu (multiple selection)

I'm trying to set up a batch menu that allows for multiple selection at once then runs all the functions. Sequence that functions are not relevant just the fact that the functions will be run with out, outside errors. Here is the code that I have so far.
#Echo off
Echo Please Enter the corrasponding numbers separated by a space or colon (,)
Echo for the Options that you would like to run e.g. 1 4,3 2
Echo Option #1
Echo Option #2
Echo Option #3
Echo Option #4
Echo.
SET /P Selection=Please Select Restore Options?
echo You chose: %Selection%
setlocal ENABLEDELAYEDEXPANSION
Set /a index = 0
FOR %%A IN (%Selection%) DO (
SET Array[!index!] = %%A
SET /a index += 1
)
for /F "tokens=2 delims==" %%s in ('set Array[') DO (
set string=%%s
set string=%string: =%
echo %string%
Call :Opt%string%
)
pause
goto :EOF
:Opt1
ECHO Option 1's code
GOTO :EOF
:Opt2
ECHO Option 2's code
GOTO :EOF
:Opt3
ECHO Option 3's code
GOTO :EOF
:Opt4
ECHO Option 4's code
GOTO :EOF
The code I have works to the point where trying to call the Array veriable and attach it to a Call e.g. Call :Opt%%s
The probelm I have is that the array variable keeps coming out with a space proceeding the selected variable. So I have tried combating this with set string=%string:=% but I keep getting an error.
Error :
either echo is off and only opt is getting called with out the selected variable.
Help with this would be amazing, Thanks in advance.
The start of the problems is
SET Array[!index!] = %%A
------------------^-^---- = aditional spaces
This aditional spaces are used, so you end with a variable with an aditional space in its name and an aditional space in its value. So, better use
SET "Array[!index!]=%%A"
The reason for the echo error is you forget to use delayed expansion in the for %%s loop. You change the %string% variable inside the loop and try to use the changed value inside the same loop.
for /F "tokens=2 delims==" %%s in ('set Array[') DO (
set "string=%%s"
set "string=!string: =!"
echo !string!
Call :Opt!string!
)
But the corrections indicated in the original set make it unnecessary to replace the spaces.
MC ND solved most of the problems with your code.
One trivial issue - the punctuation is a comma, not a colon ;-)
But a more serious issue, what if the user entered 3 choices, and there already was a variable named Array[4]? It would run that extra value that hadn't been entered by the user. It would even attempt to run a value stored in Array[anythingGoes.
You've got the number of values stored in "index", so why not use it? A more common and simpler way to iterate the array is to use a FOR /L loop. This also preserves the original order. Your way would change the order once you get 10 or more entries. (I know you say order doesn't matter, but why change the order if you don't have to?)
setlocal enableDelayedExpansion
for /l %%N in (1 1 %index%) do (
echo !Array[%%N]!
call :Opt!Array[%%N]!
)
But I don't see a reason to mess with an array at all. Your loop that parses the user input could simply call the functions directly. Now you don't even need delayed expansion.
for %%A in (%Selection%) do (
echo %%A
call :Opt%%A
)

dos batch extract characters from string

I am reading, in a file, the first column which contains 0002C1, 0002C2, 0003C1, 0004C1
Extracting only the first 4 digits and put them in a variable.
FOR /F "tokens=1" %i IN (export.txt) DO (
echo %i
set s=%i:~0,4%
echo %s%
)
in output, the result of echo %i is correct, extracting the digits seems to be working fine also (when I try it for one entry, the result is correct) but the value of s seems to not change!
Can somebody see what the problem is?
Here is the output that I receive:
0002C1
%s%
0002C2
%s%
0003C1
%s%
0004C1
%s%
First: You need yo expand the variable using the SETLOCAL ENABLEDELAYEDEXPANSION command...
Second: you are trying to "cut" a special var (%i:~0,1%), you can't.
My solution:
#Echo OFF
:: By Elektro H#cker
FOR /F %%# IN (export.txt) DO (
Call Set "Token=%%#"
Call Set "Token=%%Token:~0,4%%"
Call Echo %%Token%%
)
Pause&Exit
There are a few issues with your script, only a few adjustments needed and it works great.
You need to use delayed expansion to access a variable that you create in a for loop.
setlocal enabledelayedexpansion
FOR /F "tokens=1" %%i IN (export.txt) DO (
echo %%i
set x=%%i
set s=!x:~0,4!
echo !s!
)
The only difference being you replace the %'s with !'s to tell cmd to use delayed expansion instead to read the variables.
When you are using a batch file you need to use double %'s for the for variables.
You also need to assign %%i to something so you can use a variable sign either side of it, in this case I have used x.

Resources