dos batch extract characters from string - batch-file

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.

Related

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

Setting up variable in for loop in batch

I am fighting with little piece of code for last two days.
In this I am not able to set variable in a for loop.
I want to assign a filename to a variable for string manipulation.
echo off
for /f %%a IN ('dir /b *_ah.ttf') DO (
set /a fName=%%~na
echo %fName%
)
When I echo fName variable I get only last filename repeatedly number of times for for loop count.
(I want to pass this variable as an argument to some batch file as follows
ttfhnt --strong-stem-width=D -i %%a %fName:~0,-3%.ttf
but its failing due to above problem)
Can somebody help me please?
When the cmd parser reads a line or a block of lines (the code inside the parenthesis), all variable reads are replaced with the value inside the variable before starting to execute the code. If the execution of the code in the block changes the value of the variable, this value can not be seen from inside the same block, as the read operation on the variable does not exist, as it was replaced with the value in the variable.
This same behaviour is seen in lines where several commands are concatenated with &. The line is fully parsed and then executed. If the first commands change the value of a variable, the later commands can not use this changed value because the read operation replace.
To solve it, you need to enable delayed expansion, and, where needed, change the syntax from %var% to !var!, indicating to the parser that the read operation needs to be delayed until the execution of the command.
And set /A is only used for arithmetic operations
setlocal enabledelayedexpansion
for /f "delims=" %%a IN ('dir /b *_ah.ttf') DO (
set "fName=%%~na"
echo "!fName!" "!fName:~0,-3!"
)
edited to adapt to comments
While for command is able to execute a command (in the OP code, the dir...), retrieve its output and then iterate over the lines in this output, the original reason for the command is to iterate over a set of files. In this form, the code can be written as
setlocal enabledelayedexpansion
for %%a IN ("*_ah.ttf") DO (
set "fName=%%~na"
echo "!fName!" "!fName:~0,-3!"
)
Now, the for command replaceable parameter will iterate over the indicated set of files. (execute for /? for a list of all the command options).
But as foxidrive points, the problem with delayed expansion are the exclamation signs. Without delayed expansion, they are another normal character, but with delayed expansion they frequently become a problem when a value containig them is assigned/echoed.
A quick test
#echo off
setlocal enabledelayedexpansion
set "test=this is a test^!"
echo ---------------------
set test
echo ---------------------
echo delayed : !test!
echo normal : %test%
for /f "delims=" %%a in ("!test!") do echo for : %%a
Will show
---------------------
test=this is a test!
---------------------
delayed : this is a test!
normal : this is a test
for : this is a test
Obviously when the value is a file name, this behaviour will make the code find or not the file.
Depending on the case different solutions can be used, but usually it involves the activation / desactivation of the delayed expansion behaviour (beware, the endlocal removes any change in environment variables from the previous setlocal).
#echo off
setlocal enabledelayedexpansion
set "test=this is a test^!"
echo ---------------------
set test
echo ---------------------
echo delayed : !test!
rem Commuted to no delayed expansion
setlocal disabledelayedexpansion
echo normal : %test%
endlocal
rem Cancelled the initial enable delayed expansion
for /f "delims=" %%a in ("!test!") do endlocal & echo for : %%a
rem The last endlocal has removed the changes to the variable
echo no data : [%test%]

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
)

Batch map and lookup with expanded variables

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...

Batch file for loop dynamic variables from file without setlocal?

I have a simple ini file... really just a key=value file that I want to use to set as variables for my script.
My ini file:
DATABASE=snoopy
My Batch file code
#echo off
SET DATABASE=woodstock
FOR /f "tokens=1,2 delims==" %%a in ('C:\mycfg.ini') do (
echo a=%%a
echo b=%%b
pause
SET %%a=%%b
ECHO DATABASE=%DATABASE%
)
The echo a and b are correct, it shows
a=DATABASE
b=snoopy
But at the end when I echo %DATABASE% after calling the SET %%a=%%b
It still shows
DATABASE=woodstock
If I use delayed expansion, it works but only locally. I need it to overwrite the global so I don't see why this shouldn't work.
Well, no need to do anything to make it work.
It works.
Your problem is that when the for block (all the lines in parenthesis in your code) are read, variables are replaced with their values, so the line echo %DATABASE% is converted in echo woodstock. BUT the variable hold the correct value, changed inside the for loop. Try to place the echo outside of the for and see what the value is.
Delayed expansion is needed when a variable is changed inside a block and it is necesary to access the changed value inside the same block.
Ok I got it now..
Had to enable delayed expansion at the top of the file rather than just on the subroutine call. Then set the %%a and %%b to delayed variables and set them equal to eachother and that worked. Final code:
#echo off
SetLocal EnableDelayedExpansion
SET DATABASE=woodstock
FOR /f "tokens=1,2 delims==" %%a in ('C:\mycfg.ini') do (
set tmpA=%%a
set tmpB=%%b
SET !tmpA!=!tmpB!
)
ECHO DATABASE=%DATABASE%
And that changes it to "snoopy"
Thanks all!

Resources