How to use the variable inside another variable - batch-file

How would I do to use the variable %number% in this case?
Set Test=%strToMeasure:~-%Number%%
Whenever I use a variable the result comes out like this:
No variable only the number:
Set Test=%strToMeasure:~-3%
With variable:
Set Test=%strToMeasure:~-%Number%%
Full Code
#echo off
Set "strToMeasure=This is a string"
call :strLen strToMeasure strlen
echo.String is %strlen% characters long
Set /A number = %strlen% - 13
Set Test=%strToMeasure:~-%Number%%
Echo %strToMeasure%
Echo %Test%
pause
exit /b
:strLen
setlocal enabledelayedexpansion
:strLen_Loop
if not "!%1:~%len%!"=="" set /A len+=1 & goto :strLen_Loop
(endlocal & set %2=%len%)
goto :eof

You have two options in order to correctly execute this line:
Set Test=%strToMeasure:~-%Number%%
1- Doubling the percent signs for the second expansion and using call command:
Call Set Test=%%strToMeasure:~-%Number%%%
2- Using Delayed Expansion and enclosing the second expansion in exclamation marks:
setlocal EnableDelayedExpansion
. . .
Set Test=!strToMeasure:~-%Number%!
The second method is the usual way to solve this problem and it run faster than the former. You may review further details of this behavior at this post.

Related

Batch - How to replace exclamation mark with expression in if with delayed expension?

In order to return parameter values i need to change a string which requires to replace exclamation marks with an expression so that these exclamation marks are escaped later in the process. The current solution doesn't work inside an if. The following working script shows the issue quite well:
#ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET string="World^! wasserschutzpolizei^!"
REM REM Works quite well here!
REM SET "return1=!string:"=""!"
REM SET "return1=%return1:!=^^^!%"
REM SET "return1=!return1:""="!"
REM ECHO !return1!
IF NOT "!string!"=="" (
SET "return1=!string:"=""!"
REM This line doesn't work because of the % % inside the if...
REM Because it is one logical line.
SET "return1=%return1:!=^^^!%"
SET "return1=!return1:""="!"
ECHO !return1!
)
EXIT /B 0
ENDLOCAL
I had one non-working approach which uses CALL SET:
CALL SET "return1=%%return1:!=^^^!%%" but this doesn't work.
For an answer consider that I need delayed expension for other stuff too.
So, how do I need to change the code, espacially line SET "return1=%return1:!=^^^!%" so that it works inside the if?
Avoid using multi-line statements in batch scripts. It's not just difficult to get them right, they are hard to debug while developing the code. Delayed expansion is most useful when you need to use variables within variable replacement expressions. Here's your code the simple way:
#setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
#set prompt=$G
set input="World^! wasserschutzpolizei^!"
#rem Works quite well here!
#set "return1=!input:"=""!"
#set "return1=%return1:!=^^^!%"
#set "return1=!return1:""="!"
#echo %return1%
#if not "!input!"=="" call :DoIt
#echo %return1%
#exit /b 0
:DoIt
#set "return1=!input:"=""!"
#set "return1=%return1:!=^^^!%"
#set "return1=!return1:""="!"
#exit
Use subroutines when a short simple expression can't be used for if/loop bodies.
If you reversed your thinking and instead of using IF NOT …(, used IF …, you wouldn't have that problem as you wouldn't be setting variables within a block:
#ECHO OFF
SET "string=World! wasserschutzpolizei!"
IF "%string%"=="" EXIT /B 1
SET "return1=%string:"=""%"
SET "return1=%return1:!=^^^!%"
SET "return1=%return1:""="%"
ECHO %return1%
PAUSE
EXIT /B 0
The string will now have pre-escaped exclamation marks ready for your later process.
Like the answer of Compo, but this one works also for more than one return variable.
IF defined string (
SET "return1=!string:"=""!"
)
IF defined string (
SET "return1=%return1:!=^^^!%"
SET "return1=!return1:""="!"
)
It splits the code into two seperate blocks, therefore the percent expansion will work

Batch - Delayed expansion doesn't work with surrounding if clause

In the following script I call a subroutine with a string, the maximal length of a substring of that string and a 3rd non-existing variable to get the substring back.
The script should check if the next character after the string with the maximal substrings length is a space and if yes, then cut the string by that space (delete space to) and returning the substring and change the passed string by cutting the substring part. Example:
string: "Hello World Bla"
substringLength: 5
CALL :get_substring string substringLength substring
=> string: "World Bla" (no space), substringLength: 5 (no change), substring: "Hello"
This works fine without if clause but it doesn't when i use an if clause, even if i use delayed expansion.
Here is the working code without if statement:
#ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET string=Hello World wasserschutzpolizei
SET /A substringLength=5
CALL :get_substring string substringLength substring
ECHO !string!
ECHO !substring!
EXIT /B 0
ENDLOCAL
:get_substring
SETLOCAL ENABLEDELAYEDEXPANSION
SET "string=!%~1!"
SET "substringLength=!%2!"
SET nextChar=!string:~%substringLength%,1!
REM IF "!nextChar!"==" " (
SET substring=!string:~0,%substringLength%!
ECHO !substring!
SET /A cutSpaceCount=!substringLength!+1
SET string=!string:~%cutSpaceCount%!
ECHO !string!
ENDLOCAL & SET %1=%string% & SET %3=%substring% & EXIT /B 0
REM ) ELSE (
REM Some other case
REM )
EXIT /B 0
This doesn't work when I comment in the if statement:
IF "!nextChar!"==" " (
SET substring=!string:~0,%substringLength%!
ECHO !substring!
SET /A cutSpaceCount=!substringLength!+1
SET string=!string:~%cutSpaceCount%!
ECHO !string!
ENDLOCAL & SET %1=%string% & SET %3=%substring% & EXIT /B 0
) ELSE (
REM Some other case
)
Why does the script doesn't work with an if-statement?
How can fix it?
Be aware that my routine should also include an else statement which I cutted out because the problem is the same.
The problem is this line:
SET string=!string:~%cutSpaceCount%!
When this line is placed inside the IF command, then the value of cutSpaceCount is changed inside the code block (parentheses) of the IF, and hence it must be expanded via !cutSpaceCount! delayed expansion, not via a %cutSpaceCount% standard expansion.
You should use something like a "double delayed expansion", that is, similar to this construct:
SET string=!string:~!cutSpaceCount!!
Of course, this don't work, so the trick is use a for command to get the value of the first delayed expansion, and then use the FOR parameter to complete the second delayed expansion:
for /F %%c in ("!cutSpaceCount!") do SET "string=!string:~%%c!"
A similar problem happen when the final values in the subroutine are returned to the calling program. This is the final working code:
#ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET string=Hello World wasserschutzpolizei
SET /A substringLength=5
CALL :get_substring string substringLength substring
ECHO !string!
ECHO !substring!
EXIT /B 0
ENDLOCAL
:get_substring
SETLOCAL ENABLEDELAYEDEXPANSION
SET "string=!%~1!"
SET "substringLength=!%2!"
SET "nextChar=!string:~%substringLength%,1!"
IF "!nextChar!"==" " (
SET "substring=!string:~0,%substringLength%!"
ECHO !substring!
SET /A cutSpaceCount=!substringLength!+1
for /F %%c in ("!cutSpaceCount!") do SET "string=!string:~%%c!"
ECHO !string!
for /F "delims=" %%s in ("!string!") do for /F "delims=" %%b in ("!substring!") do (
ENDLOCAL & SET "%1=%%s" & SET "%3=%%b" & EXIT /B 0
)
) ELSE (
ECHO Some other case
)
PS - You don't need to expand variable values in SET /A command. Instead of:
SET /A cutSpaceCount=!substringLength!+1
you may simply use:
SET /A cutSpaceCount=substringLength+1
You seem to be very confused about the sequence of operations that occurs when delayed expansion has been invoked.
First, the value of var is substituted for %var%.
Then !var! is evaluated using the results.
The scope of this sequence of operations is one logical line, which may be one physical line or any number of physical lines continued with a terminal ^ or more commonly using a parenthesised sequence of lines.
In your mainline then,
CALL :get_substring string substringLength substring
ECHO !string!
ECHO !substring!
ENDLOCAL & SET %1=%string% & SET %3=%substring% & EXIT /B 0
Since these statements are not within the same logical line, they will be individually evaluated, so !var!==%var%.
Within your subroutine (non-IF version),
SET substring=!string:~0,%substringLength%!
ECHO !substring!
SET /A cutSpaceCount=!substringLength!+1
SET string=!string:~%cutSpaceCount%!
ECHO !string!
again are individual statements. The first set will first substitute for substringlength, and then execute SET substring=!string:~0,5! as a second operation.
Each of the echoes is a stand-alone statement, and the ! could (and preferably should) be replaced by %.
The set /a statement - well, set /a allows the current value of a variable to be used undecorated, so SET /A cutSpaceCount=substringLength+1 or SET /A cutSpaceCount=%substringLength%+1could be used here with no logical effect.
ENDLOCAL & SET %1=%string% & SET %3=%substring% & EXIT /B 0 will be evaluated according to the values established by the previous code-sequence.
However when you add the if, the code is parenthesised and thus becomes one logical statement and acts differently.
The echoes then require ! because you want to display the modified values within the code-block. Since cutSpaceCount is not set at the start of the code-block, SET string=!string:~%cutSpaceCount%! will be evaluated as SET string=!string:~!
and then ENDLOCAL & SET %1=%string% & SET %3=%substring% & EXIT /B 0 will duly substitute the values of the variables as they stood when the IFwas encountered
So, a replacement routine might be
:get_substring
SETLOCAL ENABLEDELAYEDEXPANSION
SET "string=!%~1!"
SET "substringLength=!%2!"
SET "substring=!string:~0,%substringLength%!"
SET "string=!string:~%substringLength%!"
IF "%string:~0,1%"==" " SET "string=%string:~1%"
ENDLOCAL & SET "%1=%string%" & SET "%3=%substring%"
EXIT /B 0
As others already explained, the problem is the line:
SET string=!string:~%cutSpaceCount%!
because you use immediate expansion (%) for variable cutSpaceCount which is changed in the same logical line/block of code.
A possible solution is to use call like this:
call set "string=%%string:~!cutSpaceCount!%%"
call introduces another variable expansion phase, so the sequence goes as follows:
immediate expansion phase where %% becomes %:
call set "string=%string:~!cutSpaceCount!%"
then delayed expansion occurs (let us assume a sample value of 5):
call set "string=%string:~5%"
another immediate expansion phase introduced by call to finally get %string:~5%.

How to count arrays in FOR loops using a variable as index?

Okay. I got rid of most of the useless code and have been tinkering with it here and there trying to find what is the problem and I think I finally found it:
Windows' Batch can't edit variables that will be expanded if those are inside a FOR loop.
Ex:
set /a x=1
Powershell Get-Clipboard> %temp%\ffmpeglist.txt
setlocal enableExtensions enableDelayedExpansion
for /F "delims=| tokens=*" %%A in (%temp%\ffmpeglist.txt) do (
set input[!x!]=%%A
call echo !input[%x%]!
set /a x += 1
)
endlocal
Expected behavior:
g:\videos\youtube1.mp4
g:\videos\youtube2.mp4
g:\videos\youtube3.mp4
g:\videos\youtube4.mp4
g:\videos\youtube5.mp4
What I get:
g:\videos\youtube1.mp4
g:\videos\youtube1.mp4
g:\videos\youtube1.mp4
g:\videos\youtube1.mp4
g:\videos\youtube1.mp4
No matter what I do, set /a x+= 1 will not change the value of x.
Are there solutions? I'm open to anything.
EDIT
In your heavily changed batch (in fact a new question) change
call echo !input[%x%]!
to
call echo %%input[!x!]%%
If in a (code block) you might need delayed expansion to force actual values,
if at the same time using an indexed variable you need another level of delayed expansion you can accomplish with a pseudo call and doubled percent signs
Call set Input=%%input[!x!]%%
mediainfo --Output=Video;%%Height%% !input! > %temp%\ffmpegres%x%.txt
Thanks to #Aacini and #LotPings , I've arrived at the answer:
set /a x=1
Powershell Get-Clipboard> %temp%\ffmpeglist.txt
setlocal enableDelayedExpansion
for /f "delims=|" %%A in (%temp%\ffmpeglist.txt) do (
set input[!x!]="%%A"
for %%i in (!x!) do echo !input[%%i]!
set /a x =+ 1
)
The problem wasn't that BATCH couldn't
edit variables that will be expanded if those are inside a FOR loop.
But that it does it in a not very straight forward way.

How to echo a variable multiple times by multiplying it with another variable?

I am writing a batch script and I am having trouble echoing a variable, here is the script,
#echo off
set num1=1
set num2=10
set /a "out=%num1%*%num2%"
echo %out%
pause`
The output I receive is 10 which makes sense but I want it to echo 'num1' ten times instead of multiplying 'num1' by 'num2'. So I want the output to be 1111111111.
Also I don't want to loop the command 10 times as I am putting the output into a text file with 'output>> file.txt' otherwise I will end up with this in the text file,
1
1
1
1
1
1
1
1
1
1
I want to end up with 1111111111, thank you.
#ECHO OFF
SETLOCAL
set num1=1
set num2=10
SET "out="&FOR /L %%a IN (1,1,%num2%) DO CALL SET "out=%%out%%%%num1%%"
echo %out%
GOTO :EOF
The SET "out=" and FOR /L %%a IN (1,1,%num2%) DO CALL SET "out=%%out%%%%num1%%" may be on separate lines if desired. Setting out to nothing is simply a safety measure to ensure that if it contains a value, it's cleared first.
The for /L performs the call command num2 times.
The call command executes SET "out=%out%%num1%" in a subprocess because each %% is interpreted as an escaped-% (% is the escape character for %) - "escaping" a character means turning off its special meaning.
The syntax SET "var=value" (where value may be empty) is used to ensure that any stray trailing spaces are NOT included in the value assigned.
Just to show a different method with set /A and a loop:
#echo off
set /A "num1=1,num2=10,out=0"
:loop
set /a "out*=10,out+=num1,num2-=1"
If %num2% gtr 0 goto :loop
echo %out%
pause
This is the simplest way to solve this problem, using Delayed Expansion.
#echo off
setlocal EnableDelayedExpansion
set num1=1
set num2=10
set "out="
for /L %%i in (1,1,%num2%) do set "out=!out!%num1%"
echo %out%
pause
PS - The multiply term is not exact in this case; perhaps "echo a variable the times indicated by another variable" be more clear...
If what you want is to print num1 num2 times in the same line you can do something like:
#echo off
set "num1=1"
set "num2=10"
(for /L %%i in (1,1,%num2%) do set /p "=%num1%" <nul
echo()>file.txt
The command set /p "=%num1%" <nul prints the text %num1% in the current line without the LF character. So num1 gets printed num2 times in the same line.

set /A command expands with a comma for more than one var, only set does not

(set /a "m1=1,m2=2")
for /f %%c in ("%m1%%m2%") do echo %%c
pause
The brackets else where than due to the for command are used in cases, a space key should have been added.
The echo of the for command, is 12. I used the number characters to face the set /A command with decimal Expression.
When i try the same procedure only with a set for a Shell, may also be named m1 it is just possible without comma seperation.
With set command the m1 Expression would be 1 m2 2 and not two values like with a set /A SET.
Is there a way to use set only once and not only with the set /A?
As other answers and comments already indicated, there is no way to directly do this in one command, but via a procedure. The method below is the simplest one:
#echo off
rem Define the several values
set "vars=m1=1,m2=2"
rem Do it:
set "%vars:,=" & set "%"
echo m1=%m1%
echo m2=%m2%
You may remove the #echo off command and execute this program to see what exactly is executed...
As I understand the question you want something like:
set x=1,y=2
and as a result to have two variables (like set /a). The answer is no.
Though you can iterate trough expressions with plain for :
#echo off
for %%a in (
"x=1" "a=5"
"y=2" "b=6"
"z=3" "c=7"
) do set "%%~a"
echo %x% %y% %z% %a% %b% %c%
Mind that the quotes around the items are mandatory because = is a delimiter. You can put everything on line and to use as separators , ,; ,<space>
May be with a lot of variables this can save you from some writing...?
this can be rewritten like this:
#echo off
set "vars=x=1,y=2,z=3,a=5,b=6,c=7"
for %%a in ("%vars:,=","%") do set "%%~a"
echo %x% %y% %z% %a% %b% %c%
And thus you'll need to change only the vars value.

Resources