Take this simple example:
#ECHO OFF
SET /P phrase="Enter Word : "
SET /a rnum=%random% %%10 +1
ECHO %phrase%
ECHO %rnum%
SET rchar=%phrase:~0,%rnum%%
ECHO %rchar%
Pause
I just want to be able to pass that rnum variable to pick that as the character chosen from the left of that user entered word to that random character.
I can't seem to figure out how to pass that as a variable.
I tried with enabledelayedexpansion with no luck:
#ECHO OFF
SET /P Phrase="Enter Word : "
SET /a rnum=%random% %%10 +1
ECHO %phrase%
ECHO %rnum%
setlocal enabledelayedexpansion
SET rchar=!phrase:~0,%rnum%!
endlocal
ECHO %rchar%
Pause
So how do I pass rnum as a variable in this instance? Thanks for any assistance.
Here's a simple modification of your example delayed expansion code, which shows one method of maintaining your variable value beyond endlocal:
#ECHO OFF
SET /P "phrase=Enter Word : "
SET /A rnum = %RANDOM% %% 10 + 1
ECHO %phrase%
ECHO %rnum%
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%G IN ("!phrase:~0,%rnum%!") DO ENDLOCAL & SET "rchar=%%~G"
ECHO rchar=%rchar%
PAUSE
The above example should be fine, as long as the end user does not begin to input strings with problematic characters. If you wanted to make it a little more robust for such scenarios then perhaps this will help:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
:AskString
Rem Get interactive string input
Set "String="
Set /P String="Enter Word : "
If Not Defined String GoTo AskString
Set String
Rem Generate a random integer 1..10
Set /A "Integer = (%RANDOM% %% 10) + 1"
Set Integer
Rem Create a substring variable using %String% and %Integer%
Echo %%SubString%% = %%String:~0,%Integer%%%
SetLocal EnableDelayedExpansion
For /F Delims^=^ EOL^=^ UseBackQ %%G In ('"!String:~0,%Integer%!"') Do (
EndLocal
Set "SubString=%%~G"
)
Set SubString
Pause
Please note that the above code uses Set Variable to display the variable name along side its value. If your variable contains certain poison characters just using Echo %Variable% may not work, and you would probably be better off keeping delayed expansion enabled at that time.
As Compo already comments, the position of your endlocal is the problem.
You could just move the endlocal after the echo
#ECHO OFF
setlocal enabledelayedexpansion
SET /P Phrase="Enter Word : "
SET /a rnum=%random% %%10 +1
ECHO !phrase!
ECHO !rnum!
SET "rchar=!phrase:~0,%rnum%!"
ECHO !rchar!
endlocal
Related
I want to find the string A in the variable Code=AAABASDG
and count each time 1 up if "A" was found so the result should be that it outputs 4 because in Code variable there are 4 A's
Example Code :
#echo off
set /A C=0
set Code=AAABASDG
for %%i in (%Code%) do IF "%%i"=="A" set /A C=%C%+1
echo %C%
pause
You could get the length of original string A, then delete the "A" letters from the string and get the length of the result, to finally subtract both lengths.
To easily get the length of the string, you could store it in a file and then ask for the %%~Z size of the file. Here it is:
#echo off
setlocal
set "Code=AAABASDG"
> before.txt echo %code%
> after.txt echo %code:A=%
for %%b in (before.txt) do for %%a in (after.txt) do set /A "count=%%~Zb-%%~Za"
echo %count%
The only drawback of this method is that it is not case-aware: both upcase and lowcase letters are delete in the replacement operation
#echo off
set /A C=0
set "Code=AAABASDG"
:loop
if defined code (
if "%code:~-1%"=="A" set /a C+=1
set "code=%code:~0,-1%"
goto loop
)
echo %C%
Use set "var=value" for setting string values - this avoids problems caused by trailing spaces. Don't assign " or a terminal backslash or Space. Build pathnames from the elements - counterintuitively, it is likely to make the process easier.
Substrings in batch are obtained from %var:~m,n% where ,n is optional; m is count-of-chars-from-beginning-of-string, from end if negative. ,n positive = max length to return; negative = end-position in chars from end; missing=return all after m
Here's a quick example which gets help from PowerShell:
#Echo Off
SetLocal EnableExtensions
Set "Code=AAABASDG"
For /F %%G In ('%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe
-NoProfile "[RegEx]::Matches('%Code%','A').Count"') Do Set "C=%%G"
Echo(%C%
Pause
I have searched this everywhere and I didn't found it, so what I want to do is simple, I want to count the underscores in a filename and put it in a variable to later use it.
Is there any simple batch code to do it ?
#echo off
setlocal enabledelayedexpansion
set filename=__example_file.bin_
set cnt=0
set pos=0
:loop
if "!filename:~%pos%,1!"=="_" set /a cnt=%cnt%+1
set /a pos=%pos%+1
if not "!filename:~%pos%,1!"=="" goto loop
echo Count: %cnt%
That code is pretty ugly but I can't find a better way so far.
A different method to count underscores (or more exact elements separated by an underscore)
is to use self expanding code:
:: Q:\Test\2017\08\28\SO_45917406.cmd
#echo off & setlocal enabledelayedexpansion
set "FileName=example_file_name_20170828_181000.txt"
Set i=1&Set "FileName[!i!]=%FileName:_="&Set /a i+=1&Set "FileName[!i!]=%"
Echo Counted %i% underscore separated elements
Set FileName
Sample output:
> Q:\Test\2017\08\28\SO_45917406.cmd
Counted 5 underscore separated elements
FileName=example_file_name_20170828_181000.txt
FileName[1]=example
FileName[2]=file
FileName[3]=name
FileName[4]=20170828
FileName[5]=181000.txt
This solution is inspired from #xmcp :
#echo off
Rem The srting to count in the filename is the underscrore "_"
Rem we can of course set another one ;)
set "MyString=_"
setlocal enabledelayedexpansion
#for /f "delims=" %%a in ('Dir /b "%userprofile%\Desktop"') do (
set "filename=%%a"
set /a "cnt=0"
set /a "pos=0"
Call:Counting_String "!filename!" "%Mystring%"
)
pause & exit
::*********************************************************
:Counting_String <filename> <MyString>
set "filename=%~1"
set "string=%~2"
if /I "!filename:~%pos%,1!"=="%Mystring%" set /a cnt+=1
set /a pos+=1
if not "!filename:~%pos%,1!"=="" goto Counting_String
echo !filename!: [!cnt!] "%Mystring%"
exit /b
::*********************************************************
Hi I managed to get the code below to create a list of numbers and place a comma at the end of each number created However, it has proven to be quite the challenge to get them on the same line separated by a ,[space]
#ECHO OFF
setlocal EnableDelayedExpansion
set _Output=%UserProfile%\Desktop\NumGen.txt
::only change these three lines
set "start=1" ::starts from this number
set "amount=10" ::amount of files created
set "length=5" ::length of fileNames
set "join_with=, " ::what to join each number with
set /a "last=%start%+%amount%"
for /l %%i in (%start%,1,%last%) do (
set "folderName=0000000000%%i"
set "folderName=!folderName:~-%length%!%join_with%"
>>"%_Output%" ECHO.!folderName!
)
pause
so my output at the moment is
00001,
00002,
00003,
00004,
00005,
00006,
00007,
00008,
00009,
00010,
00011,
However I would like it to be
00001, 00002, 00003, 00004, 00005, 00006, 00007, 00008, 00009, 00010, 00011
I have windows 10 64bit. Any help will be appreciated
echo is not able to do that. But there is a workaround, (ab)using the set command:
for /l %%i in (1,1,5) do (
<nul set /p "=%%i, "
)
echo(
After a long break I decided to give this another bash and presto. Success :-)
::CallScript
#ECHO OFF
CALL :ScriptA
CALL :ScriptB
CALL :ScriptC
pause
goto :eof
:ScriptA
#ECHO OFF
setlocal EnableDelayedExpansion
set _Output=%UserProfile%\Desktop\NumGen.txt
::only change these three lines
set "start=1"
set "amount=10"
set "length=5"
set "join_with=, "
set /a "last=%start%+%amount%"
for /l %%i in (%start%,1,%last%) do (
set "folderName=0000000000%%i"
set "folderName=!folderName:~-%length%!%join_with%"
>>"%_Output%" ECHO.!folderName!
)
goto :eof
:ScriptB
#ECHO OFF
setlocal enableextensions enabledelayedexpansion
set "var="
for /f "usebackq delims=" %%a in ("%UserProfile%\Desktop\NumGen.txt") do set "var=!var!%%a"
echo %var%> "%UserProfile%\Desktop\NumList.txt"
goto :eof
:ScriptC
#ECHO OFF
del "%UserProfile%\Desktop\NumGen.txt"
goto :eof
This code determines the starting number, the amount of numbers, the length of the numbers and joins them with , "
Then ScriptB concatenates each number together with the , " and saves to NumList.txt
Lastly the script deletes the NumGen.txt file.
Stephan has one solution with SET /P. The other option is to build the entire string within an environment variable, and then write after the loop ends. This is significantly faster, but it will fail if the final string length exceeds ~8191 bytes.
set "str="
for /l %%i in (%start%,1,%last%) do (
set "folderName=0000000000%%i"
set "str=!str!!folderName:~-%length%!%join_with%"
)
>>"%_Output%" ECHO.!str!
You could remove the unwanted trailing , if you want:
>>"%_Output%" ECHO.!str:~0,-2!
I know how to do a literal string substitution in a batch script. However, I have a specific situation where I need to substitute the value of a numeric variable
This is the script:
setlocal enableextensions enabledelayedexpansion
set /A L=2
:L1
if %L% EQU 0 goto :EOF
set STRING="THIS IS # TEST"
SET NEW=%STRING:#=%%L%
echo %NEW%
set /A L=%L% - 1
goto L1
I want it to display this:
THIS IS 2 TEST
THIS IS 1 TEST
But it ends up diplaying this instead:
THIS IS TEST2
THIS IS TEST1
Any tips on how to get it to do what I need?
Thanks.
Even the solutions of aphoria and Bali C will work, it's better to use
set "NEW=!STRING:#=%L%!"
As then the replacement will be done in the delayed expansion phase and not in the percent expansion phase.
This will also work with exclamation marks and carets in STRING
#echo off
set L=2
set "String=This is # test!"
setlocal EnableDelayedExpansion
set "NEW=!STRING:#=%L%!"
echo !NEW!
Your almost there, just change
SET NEW=%STRING:#=%%L%
to
SET NEW=%STRING:#=!L!%
You need to use !L! to use delayed expansion.
#ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET /A L=2
:L1
IF !L! EQU 0 goto :EOF
SET STRING=THIS IS # TEST
SET NEW=%STRING:#=!L!%
ECHO %NEW%
SET /A L=!L! - 1
GOTO L1
Actually, you don't have to use !L! everywhere, just in the SET NEW=%STRING:#!L!% line. I used it everywhere for visual consistency.
Just for fun. Here is how to do it without delayed expansion. :)
Using the call command to double expand the variables. Use double percents %% around the variable to evaluate second. Single percents % around the variable to evaluate first.
setlocal EnableExtensions
set /A L=2
:L1
if %L% EQU 0 goto :EOF
set "STRING=THIS IS # TEST"
call set "NEW=%%STRING:#=%L%%%"
echo %NEW%
set /A L=%L% - 1
goto L1
You may also use the STRING as a "format string" placing the desired variables in the right places enclosed in exclamation marks. This way, no further replacement of the values is needed, just display the format string in the usual way:
rem Define the "format string" with Delayed Expansion disabled:
set STRING=THIS IS !L! TEST
setlocal enableextensions enabledelayedexpansion
set /A L=2
:L1
if %L% EQU 0 goto :EOF
echo %STRING%
set /A L=L - 1
goto L1
Or, with no Delayed\Expansion:
set STRING=THIS IS %%L%% TEST
set /A L=2
:L1
if %L% EQU 0 goto :EOF
call echo %STRING%
set /A L=L - 1
goto L1
Antonio
I am translating a shell script to windows batch. What I need to do is take all except 1,2 and last from command line arguments. join them and send to another program as argv.
#echo off
SET subject=%1
set count=%2
set candidates=""
set /a i=0
set /a c=0
FOR %%A IN (%*) DO (
ECHO %%A
set /a i+=1
IF %i% geq 2 (
set /a c+=1;
set candidates[!c!]=%%A
)
)
SET /a count_actual=(%i%-3)
SET /a count_expected=%count%
echo %count_expected%
echo %count_actual%
echo %subject%
echo %candidates%
I want the candidates array be argv[3..n-1]
e.g. If I write batch x 2 a b p it should pass a b to that another program
The problem is loop counter is not being incremented by += operator. If I write echo %1% inside FOR I see 0 always
You should not use for %%A in (%*) as it treats %* as filename set. This may cause problems, especially if you can pass * or ? (wildcard match characters in cmd) in parameters - as they will be expanded to all files satisfying pattern. Second, batch does really know nothing about arrays - a[1] and a[2] are just a shorthand notation for humans - they are two distinct variables.
Given the problem Parse command line, take second parameter as count of parameters to concatenate into a variable, here is my take:
#echo off
setlocal
set subject=%1
shift
set exp_count=%1
if not defined exp_count (
echo Count not specified
exit /b 1
)
set /a "verify=%exp_count%"
if %verify% leq 0 (
echo Count not valid /not a positive integer/
exit /b 2
)
set real_count=0
:loop
shift
if "%~1"=="" goto end_params
set /a real_count+=1
if %real_count% leq %exp_count% set "candidates=%candidates%%~1"
goto loop
)
:end_params
if %real_count% lss %exp_count% (
echo Less parameters passed than specified!
exit /b 3
)
echo %subject%
echo %candidates%
Please note I'm not checking if there is a 'hanging' parameter (the last, not being concatenated) but it should be trivial to add that check. I left it out on purpose to make the code more flexible.
I have two answers for your question:
1- The first problem is that in IF %i% ... command the value of i variable not change (although set /a i+=1 command will correctly increment the variable) and the way to solve it is by including setlocal EnableDelayedExpansion command at beginning and enclose i in percents signs this way: IF !i! ... (as said in previous answers). However, you must note that an array variable in Batch is different than a simple variable with same name (they both can exist at same time), so array elements must always be written with subscripts and there is NO way to process an entire array in a single operation. See this topic for further details.
In your program you must transfer the elements of candidates array into a simple variable, that in the example below have the same name (just to state my point):
#echo off
setlocal EnableDelayedExpansion
SET subject=%1
set count=%2
set candidates=""
set /a i=0
set /a c=0
FOR %%A IN (%*) DO (
ECHO %%A
set /a i+=1
IF !i! geq 2 (
set /a c+=1
set candidates[!c!]=%%A
)
)
SET /a count_actual=(%i%-3)
SET /a count_expected=%count%
echo %count_expected%
echo %count_actual%
echo %subject%
REM Transfer "candidates" array elements into "candidates" simple variable:
set candidates=
FOR /L %%i IN (1,1,%c%) do (
set candidates=!candidates! !candidates[%%i]!
)
REM Show "candidates" simple variable:
echo %candidates%
Note that in Batch files you may insert commas, semicolons and equal-signs as separators instead spaces in most commands. However, SET /A command have other rules at this respect, so the semicolon must be omitted.
2- Independently of the array management explained above, this is the way I would solve your problem using a list instead of an array:
#echo off
SET subject=%1
shift
set count=%1
set candidates=
set lastArg=
set i=0
:nextArg
shift
if "%1" equ "" goto endArgv
set /a i+=1
set candidates=!candidates! !lastArg!
set lastArg=%1
goto nextArg
:endArgv
SET /a count_actual=i-3, count_expected=count
echo %count_expected%
echo %count_actual%
echo %subject%
echo %candidates%
Antonio
Yes your code will not increment i. Batch variable replacement occurs when a block is parsed, not when it is executed. The entire for block is parsed once, so %i% is replaced with zero before the for block is executed.
To disable that you need to enable delayed expansion and change your variable escape characters from %'s to !'s to have the replacement made at runtime. Then you will see i incremented in the for loop.
#echo off
Setlocal EnableDelayedExpansion
SET subject=%1
set count=%2
set candidates=""
set /a i=0
set /a c=0
FOR %%A IN (%*) DO (
ECHO %%A
set /a i+=1
IF !i! geq 2 (
set /a c+=1
set candidates[!c!]=%%A
)
)
SET /a count_actual=(%i%-3)
SET /a count_expected=%count%
echo %count_expected%
echo %count_actual%
echo %subject%
echo %candidates%
You will also need to get rid of the ; at the end of the set /a c+=1; line and I'm not sure what you are trying to do on line set candidates[!c!]=%%A as the brackets don't mean anything in batch.
While there are a bunch of answers already listed, I decided to add one more. My approach is to keep the answer as simple as possible for your specific needs. If you have any questions feel free to ask.
This will create the array as you desired [3,...,n-1] without the need for delayed expansion or fancy logic.
#echo off
:: Get the First Two Parameters
set "subject=%1"
shift
set "count=%1"
shift
:: Loop through the rest
set "index=0"
:NextParam
set "param=%1"
shift
set "next=%1"
:: Skip the last parameter
if not defined next goto EndParam
set "candidates[%index%]=%param%"
set /a "index+=1"
goto NextParam
:EndParam
set "count_actual=%index%"
set "count_expected=%count%"
:: Show the Results
echo %count_actual%
echo %count_expected%
echo %subject%
set candidates
Here is an alternate where the candidates are stored in a space delimited string instead of seperate variables. Replace the space between the %candidates% %param% to whatever delimiter you desire.
#echo off
:: Get the First Two Parameters
set "subject=%1"
shift
set "count=%1"
shift
:: Loop through the rest
set "index=0"
:NextParam
set "param=%1"
shift
set "next=%1"
:: Skip the last parameter
if not defined next goto EndParam
set "candidates=%candidates% %param%"
set /a "index+=1"
goto NextParam
:EndParam
set "count_actual=%index%"
set "count_expected=%count%"
:: Show Results
echo %count_actual%
echo %count_expected%
echo %subject%
echo %candidates%