I have a bit of (Bad) Code for encrypting text, but to be able to decrypt it needs to have something inbetween the numbers. I want to fit random letters inbetween the numbers so it looks less obvious, this is where i got to:
#echo off
setlocal enableDelayedExpansion
set /p code=Text:
set chars=0123456789abcdefghijklmnopqrstuvwxyz
for /L %%N in (10 1 36) do (
for /F %%C in ("!chars:~%%N,1!") do (
Set _Alphanumeric=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
Set _count=0
set _RNDLen=%random%
Set /A _RNDLen=_RNDLen%%4
If !_count! leq %_RNDLen% call :loop
set "code=!code:%%C=-%%N!"
)
)
echo !code!
echo !_str!
pause
:loop
Set /a _count+=1
set _RND=%random%
Set /A _RND=_RND%%51
SET _str=!_str!!_Alphanumeric:~%_RND%,1!
EXIT /B %ERRORLEVEL%
The problem is that the program just quits before giving any output, even if i remove the exit /b statement. Thanks for help
I've no idea what principle you're using for your algorithm, but fundamentally you need to understand delayed expansion.
When your outer loop, for %%N is parsed, every %var% is replaced by the contents of that variable at that time, hence
set _RNDLen=%random%
If !_count! leq %_RNDLen% call :loop
are replaed by
set _RNDLen=a specific random number
If !_count! leq call :loop
The first line here will set _rndlen to the same number every time (for ny run) and since _rndlen is undefined at the start of the loop, it willl be replaced by nothing, hence the if statement has faulty syntax and hence cmd objects and would display a message.
You can use !random! with delayed expansion invoked to select a rendom number each time, and you need !_rndlen! to access the changed value of _rndlen (changed from its original value of nothing to some random value and then mod-4'd)
Personally, I'd assign _alphanumeric outside of the (outer) loop since its value isn't varied by the loop operation.
And naturally, you know that when you hit Return following the pause, the loop code will be executed before the routine terminates (by flow-through) and you should include a
goto :eof
line after the pause to skip this last operation.
Related
I am trying to substring a string from a text file stored in a variable "user" each loop the substring parameters is increment so that i can process next data. However, the variable parameter "Inc" that i am giving to the sub-string command doesn't seem to update its value within the for loop.
I already used the SETLOCAL EnableDelayedExpansion but with no success. Below is piece of the code:
Note: "Inc" is defined previously as integer and have initial value of 21.
I expect that each loop the variable 'test' should be: substring of "user" 41,20 .. 61,20 .. 81,20 .. and so on.. But the problem is that for the all 12 loops "Inc" in the substring command keeps the value 41 and never increases even though the echo Inc command below shows that it does increase.
Appreciate your help in this issue.
:setfunc
echo. >>parsed.txt
IF !inew!==48 (
for /L %%g IN (1,1,12) do (
Set test=!user:~%Inc%,20!
echo !test!
echo !user:~%Inc%,20! >>parsed.txt
Set /a Inc=!Inc!+20
echo count=%%g
echo Inc=!Inc!
)
The problem is the immediate %-expansion you are using for a variable (Inc) whose value is changing in the same block of code, namely the for /L loop.
To solve this you need another layer of variable expansion. There are the following options:
Add another for loop and use its meta-variable expansion, which happens before delayed expansion:
> "parsed.txt" (
for /L %%g in (1,1,12) do (
for %%i in (!Inc!) do (
echo(!user:~%%i,20!
set /A "Inc+=20"
)
)
)
This is the preferred method in my opinion as it is the fastest one.
Use call, which initiates another %-expansion that happens after delayed expansion:
> "parsed.txt" (
for /L %%g in (1,1,12) do (
call echo(%%user:~!Inc!,20%%
set /A "Inc+=20"
)
)
The doubled %-signs are needed to escape the first %-expansion phase and literally leave % behind.
Since Inc is purely numeric (or more precisely, a signed 32-bit integer), you could also use for /F together with set /A:
> "parsed.txt" (
for /L %%g in (1,1,12) do (
for /F %%i in ('set /A "Inc"') do (
echo(!user:~%%i,20!
set /A "Inc+=20"
)
)
)
This works since set /A is executed in cmd-context rather than in batch-context when run by for /F, where it returns its (final) result, which in turn is then captured by for /F.
This is not a quite practical approach in this situation here, though it might be considered when there is a for /F loop involved anyway.
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.
Im trying to make a CMD batch script that will do the following.
Read the first line of a text file. The first line of the text file contains a date.
Delete the text file if the date is 3 months old from current date.
For illustration,
the first line of file A is Hello1, the first line of file B is Hello2
I want to get an output showing this
%counter% %first line of text file%,
so for my example it should look like this:
2 Hello2
1 Hello1
but instead, i am getting this:
2
1 Hello2
My current code is this.
set file.1=A.txt
set file.2=B.txt
set counter=2
SETLOCAL EnableDelayedExpansion
set counter=%counter%
:loop
if %counter% NEQ 0 (
set /p texte=<!file.%counter%!
echo %counter% %texte%
set /a counter=%counter%-1
gotop loop)
How do I fix this?
You have set it up for delayed expansion with your setlocal command (which should probably have a corresponding endlocal by the way) but you don't appear to be using delayed expansion in all the places it's needed.
Delayed expansion of variables requires the use of ! for expansion, not %.
Of course, once you do that, you're going to find issues with an expression like !file.!counter!! because cmd.exe is not the, err, greatest tool in the world :-)
However, that fact has produced some of the sneakiest coders in the world by forcing them to work around such limitations and you can do double-indirection of variables by using call as per the following program:
#setlocal enableextensions enabledelayedexpansion
#echo off
set file.1=A.txt
set file.2=B.txt
set counter=2
:loop
if !counter! NEQ 0 (
call :sneaky_set fspec file.!counter!
set /p texte=<!fspec!
echo !counter! !texte!
set /a counter=!counter!-1
goto loop
)
endlocal
goto :eof
:sneaky_set
set %1=!%2!
goto :eof
The call statement there passesfspec and file.N (the first level of indirection and where N is the current value in counter) to sneaky_set. It in turn executes:
set fspec=!file.N!
which is the second level of indirection and therefore sets file to the correct *.txt value.
::Compare with available valid arguments
FOR /L %%i IN (1,1,!avArgc!) DO (
FOR /L %%j IN (1,1,!argc!) DO (
IF !avArgv[%%i]!==!argv[%%j]! (
echo Match: !avArgv[%%i]!
::Check the next option
SET /A nextArg=%%j
SET /A nextArg+=1
FOR /L %%n IN (!nextArg!,1,!nextArg!) DO (
IF !nextArg! LEQ !argc! (
echo next arg: !argv[%%n]!
call :CheckSubOption
)
)
)
)
)
In my above code example - How do I take for loop variable like %%j and increment itself within the for loop like this %%j++ ? Current solution that I have (which is messy and I don't like it) is to create a new variable and set it to the value of %%j and then increment that variable and start using that variable like this:
::Check the next option
SET /A nextArg=%%j
SET /A nextArg+=1
Observing your code and your intention, it would seem that you would want to skip numbers during the loop structure. The way you want to change it though would be destabilizing. In most scripting languages such as matlab,bash, and batch, the variable that is used in for-loops serves as a frame of reference within the loop. When you tell the code to run a particular for-loop, it will run that computation regardless if the parameters of it changed. A real world example of this is the professor who is using outdated figures to solve a problem and it isnt until the next day he receives the new figures. The professor cant change his answer accordingly because he doesnt have the new data yet.
This does not mean this problem is unsolvable. In fact there are a variety of ways to approach this. The first one which is a little more complicated involves a nested For structure.
#echo off
set /p maxLength=[Hi times?]
set skip=0
FOR /L %%i IN (1,1,%maxLength%) DO (call :subroutine %%i)
echo alright im done.
pause
GOTO :eof
rem the below code uses a for loop structure that only loops 1 time based on the passed argument from the overall for loop as so to make changes to how its run.
:subroutine
set /a next=%1+%skip%
FOR /L %%r IN (%next%,1,%next%+1) DO (call :routine %%r)
GOTO :eof
:routine
if %1==3 (set /a skip=1)
echo %skip%
echo %next%
echo %1
pause
GOTO :eof
When running the program, the variable next will skip the value of 3 if the maxlength variable is greater than 3.
The reason this is so is because the nested for-loop only runs once
per iteration of the overall for loop
. This gives the program time to reset the data it uses, thanks to the call command which serves as a way to update the variables. This however is extremely inefficient and can be done in much less lines of code.
The second example uses GOTO's and if statements.
#echo off
set jump=1
:heyman
set /A "x+=%jump%"
if %x%==4 (set /A "jump=2")
echo %x%
if %x% LSS 10 goto heyman
echo done!
This code will essentially echo the value of x thats incremented each time until it reaches the value of 10. However when it reaches 4, the increment increases by 1 so each time it runs the loop increments the x value by 2. From what you wanted, you wanted to be able to change the way the value of %%j increments, which can not be done as %%j is a statement of where the for-loop is in its computation. There is no difference in what can be accomplished with for-loops and goto statements except in how they are handled.
While i unfortunately don't have the correct form of your code yet, i know that code examples i have given can be utilized to achieve your particular desire.
The general solution for thoses case is to not rely on blocks inside loops/if but instead to use subroutines where you are not blocked by the level of evaluation.
FOR /L %%i IN (1,1,!avArgc!) DO call :Loop1 %%i
goto :EOF
:Loop1
FOR /L %%j IN (1,1,!argc!) DO call :Loop2 %1 %%j
goto :EOF
:Loop2
IF !avArgv[%1]!==!argv[%2]! (
echo Match: !avArgv[%1]!
::Check the next option
SET /A nextArg=%2+1
call :CheckOpt %nextArg%
)
goto :EOF
:CheckOpt
IF %1 LEQ %argc% (
echo next arg: !argv[%1]!
call :CheckSubOption
)
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
)