I'm trying to loop through the arguments that I am passing to a batch file. Based on the argument, I want to set a variable flag true or false for use later in the script
So my command is "myscript.bat /u /p /s"
And my code is:
FOR /f %%a IN ("%*") DO (
IF /I "%%a"=="/u" SET UPDATE=Y
IF /I "%%a"=="/p" SET PRIMARY=Y
IF /I "%%a"=="/s" SET SECONDARY=Y
)
It only works if i have a single argument, which tells me that it is getting the entire list of arguments as a single argument. I've tried "delims= " but to no avail. Any thoughts on getting each spaced argument?
What about adding a value to one of the params?
myscript.bat /u /p /d TEST /s
:loop
IF "%~1"=="" GOTO cont
IF /I "%~1"=="/u" SET UPDATE=Y
IF /I "%~1"=="/p" SET PRIMARY=Y
IF /I "%~1"=="/s" SET SECONDARY=Y
IF /I "%~1"=="/d" SHIFT & SET DISTRO="%~1"
SHIFT & GOTO loop
:cont
But the SHIFT that comes inline with the last IF doesn't actually shift anything. DISTRO ends up being "/d" instead of "TEST"
You're not too far off on your original piece, and since I dislike GOTO loops, I thought I'd post this:
FOR %%a IN (%*) DO (
IF /I "%%a"=="/u" SET UPDATE=Y
IF /I "%%a"=="/p" SET PRIMARY=Y
IF /I "%%a"=="/s" SET SECONDARY=Y
)
The reason it was only working with one parameter is the over-use of quotes. By putting %* in quotes you were making the entire commandline one single token. also, the /F variant of FOR isn't what you were looking for either. The documentation available from FOR /? should help clear things up.
You could loop over the arguments using SHIFT, GOTO and an extra IF to check if there are no more parameters to parse:
:loop
IF "%~1"=="" GOTO cont
IF /I "%~1"=="/u" SET UPDATE=Y
IF /I "%~1"=="/p" SET PRIMARY=Y
IF /I "%~1"=="/s" SET SECONDARY=Y
SHIFT & GOTO loop
:cont
...
UPDATE (addressing the case when a parameter has an argument of its own)
The SHIFT in the IF statement that checks for /d does work. The issue is that the entire line is evaluated at once and both instances of %~1 get replaced with the same value, which is /d at that point.
So, basically the solution in this case would be to cause the interpreter to evaluate the SET DISTRO="%~1" part separately from the IF /I "%~1"=="/d". There can be various approaches to this. For instance, you could simply move SHIFT & SET DISTRO="%~1" to the next line and skip it if %~1 is not /d:
...
IF /I NOT "%~1"=="/d" GOTO skip_d
SHIFT & SET "DISTRO=%~1"
:skip_d
...
Another method could be to assign a special value (e.g. a ?) to DISTRO and shift when /d is encountered. Then, on the next line, check if DISTRO has that special value and set it to %~1:
...
IF /I "%~1"=="/d" SHIFT & SET DISTRO=?
IF "%DISTRO%"=="?" SET "DISTRO=%~1"
...
Related
I have written small script for converting text to uppercase as shown below and saved this file as .BAT extension
`converttoupper.bat`
I want user to try "help" command, so that they get the help on syntax for using the commands as shown below
help converttoupper
something like this
# help converttoupper
For more information on a specific command, type HELP command-name
CONVERTTOUPPER This converts the text to upper case
Update
I am fine even if I get something as shown below. I do not want to overwrite any windows command.
helpme converttoupper
or
helpme connectvpn
I have many BAT files, and wish to display respective helps when each executed.
You can create a "fake" function. Let's call this define.cmd and place it in %systemroot%\system32
We add the code:
#echo off
for /f "tokens=1,*delims=? " %%i in ('type "%~1" ^|findstr ":?"') do echo %%j
Then in all your batch files you want people to read the help for, add the help lines by starting them off with a :? using your convertoupper.cmd file as example:
#echo off & set upper=
if "%~1" == "" echo incorrect usage & call define.cmd "%0"
if "%~1" == "/?" call define.cmd "%0"
for /f "skip=2 delims=" %%I in ('tree "\%~1"') do if not defined upper set "upper=%%~I"
set "upper=%upper:~3%"
echo %upper%
goto :eof
:? # help converttoupper
:? "define %0" or "%0 /?" will display this help content
:? For more information on a specific command, type HELP command-name
:? CONVERTTOUPPER This converts the text to upper case
Now you can run define converttoupper or converttoupper /?. If you run converttoupper without any arguments, it will also display the same help.
Below is an example of safely handling arg capture and help enqueries.
After arguments are safely Captured, Findstr is used to test the content for valid help switches:
Set Args | %SystemRoot%\System32\Findstr.exe /bli "Args=\/? Args=-? Args=Help?" > nul && (Rem commands)
Set Args : allows the argument string to be piped to findstr without any risk of failure due to poison characters.
/bli : findstr sawitches : match literal string at beginning of line ignoring case.
"Args=\/? Args=-? Args=Help?" : Space delimited list of strings to match; treated as match string a or b or c
> nul : Suppress the output of any match
&& : Conditional operator; 'On command success'
Note: Terminating each help switch with ? allows use of substring modification to remove the leading switch and space and directly Call a label prefixed with the query keyword
#Echo off & SETLOCAL
=========================================================================
Rem -- Arg capture method is a modified version of Dave Benhams method:
Rem -- https://www.dostips.com/forum/viewtopic.php?t=4288#p23980
SETLOCAL EnableDelayedExpansion
1>"%~f0:Params.dat" <"%~f0:Params.dat" (
SETLOCAL DisableExtensions
Set prompt=#
Echo on
For %%a in (%%a) do rem . %*.
Echo off
ENDLOCAL
Set /p "Args="
Set /p "Args="
Set "Args=!Args:~7,-2!"
#Rem duplicate Args for the purpose of counting doublequotes [destructive].
Set "DQcount=!Args!"
) || (
Echo(%~nx0 requires an NTFS drive system to function as intended.
CMD /C Exit -1073741510
) || Goto:Eof
If Not defined Args Goto:NoArgs
REM substitute doublequotes in Args clone 'DQcount'; count substring in string;
REM assess if count is even; If false "||": Remove doublequotes from string. If true "&&" and if entire
REM arg line is doublequoted, remove outer quotes.
Set Div="is=#", "1/(is<<9)"
Set "{DQ}=0"
Set ^"DQcount=!DQcount:"={DQ}!"
2> nul Set "null=%DQcount:{DQ}=" & Set /A {DQ}+=1& set "null=%"
Set /A !Div:#={DQ} %% 2! 2> nul && Set ^"Args=!Args:"=!" || If [^%Args:~0,1%^%Args:~-1%] == [""] Set "Args=!Args:~1,-1!")
For /f Delims^= %%G in ("!Args!")Do Endlocal & Set "Args=%%G" 2> nul
:NoArgs
=====================================================================
Rem help query assessment
(
Set Args | %SystemRoot%\System32\Findstr.exe /bli "Args=\/? Args=-? Args=Help?" > nul && (
Rem Args value has leading /? -? or help?
If not "%Args:*?=%"=="" (
Rem Args value contains leading /? -? or help? with additional Parameter
Call:%Args:*? =%_Syntax && Goto:Eof || (
Rem quit after Call to Syntax info if valid Parameter; else notify invalid and show valid syntax queries.
Echo(Invalid query: "%Args:*? =%" : Does not Match a valid Help Query:
)
)
Rem show valid syntax queries.
For /F "Tokens=1 Delims=:_" %%G in ('%SystemRoot%\System32\Findstr.exe /R "^:.*_Syntax" "%~f0"') Do Echo(%~nx0 /? %%G
ENDLOCAL & Exit /b 0
)
) 2> nul
Set Args
Goto:Eof
Rem Demo syntax labels
:Demo_Syntax
Echo %~0 help info
Exit /b 0
:Example_Syntax
Echo %~0 help info
Exit /b 0
I come to find some guidance on accomplishing the following:
I have a variable with content like this:
varname = asdfiuytgy12$gggsy22.oihbcxew
or
varname = oiujedc$thisisit.oiju
which $ and . are exactly my partters and I need to get what is within them so gggsy22 or thisisit.
I need to use batch to create a simple bat file. I hope someone can provide some guidance.
Edit - (from comment section)
Actually a friend of mine helped and it did work but with a quite amount of lines:
Set "sstr=$"
SET stemp=%nameVar%&SET pos=0
:loop
SET /a pos+=1
echo %stemp%|FINDSTR /b /c:"%sstr%" >NUL
IF ERRORLEVEL 1 (
SET stemp=%stemp:~1%
IF DEFINED stemp GOTO loop
SET pos=0
)
Set "pos1=%pos%"
Set "sstr=."
SET stemp=%nameVar%&SET pos=0
:loop
SET /a pos+=1
echo %stemp%|FINDSTR /b /c:"%sstr%" >NUL
IF ERRORLEVEL 1 (
SET stemp=%stemp:~1%
IF DEFINED stemp GOTO loop
SET pos=0
)
Set "pos2=%pos%"
set /a "pos2=%pos2%-%pos1%-1"
call set env=%%nameVar:~%pos1%,%pos2%%%
#echo off
set "varname=asdfiuytgy12$gggsy22.oihbcxew"
for /f "tokens=2 delims=$." %%a in ("%varname%") do set "sub=%%a"
The following works in nearly any situation. The only thing that could break the code is if the string contains a quote " followed by a poison character like &, |, etc.
#echo off
setlocal
set "str=oiujedc$thisisit.oiju"
:: Verify string exists and has the proper format
echo "%str%"|findstr "\$.*\." >nul || (echo Value not found & exit /b)
:: Extract the value
:: The extra "x" is needed in case there is no character between $ and .,
:: in which case the result should be No Value (result variable not defined)
for /f "delims=." %%A in ("x%str:*$=%") do set "val=%%A"
set "val=%val:~1%"
:: Show the result
echo value = "%val%"
A bullet proof variant can be made by incorporating delayed expansion.
I would like split a string in two part with = as delimiter.
I saw this post but I do not manage ta adapt.
I try this:
set "str=4567=abcde"
echo %str%
set "var1=%str:^=="^&REM #%
echo var1=%var1%
Why it does not work?
While not a bulletproof solution (use the for, artoon), without more info, this can do the work
#echo off
setlocal enableextensions enabledelayedexpansion
set "str=4567=abcde"
rem Step 1 - remove the left part
set "str1=!str:%str%!"
rem Step 2 - Get the right part
set "right=!str:*%str1%!"
rem Step 3 - Get the left part
set "left=!str:%right%=!"
set "left=%left:~0,-1%"
echo [%left%] [%right%]
edited to adapt to comments (OP code in comments adapted to my code, or the reverse)
for /f "delims=" %%i in ('set') do (
setlocal enabledelayedexpansion
rem Step 1 - remove the left part
set "str=%%i"
for %%x in ("!str!") do set "str1=!str:%%~x!"
rem Step 2 - Get the right part
for %%x in ("!str1!") do set "right=!str:*%%~x!"
rem Step 3 - Get the left part
for %%x in ("!right!") do set "left=!str:%%~x=!"
set "left=!left:~0,-1!"
echo [!left!] [!right!]
endlocal
)
And no, as previously indicated this is not bulletproof and some of the variables show problems (had I said it is not bulletproof?).
What i don't understand is the requirement to not use a for loop and then use a for loop. It is a lot easier this way
for /f "tokens=1,* delims==" %%a in ('set') do (
echo [%%a] [%%b]
)
Another alternative (not as easy as the for, more stable than the previous one, non bulletproof) is
for /f %%a in ('set') do (
call :split left right %%a
echo [!left!] [!right!]
)
goto :eof
:split leftVar rightVar data
set "%~1=%~3"
setlocal enabledelayedexpansion
set "data=%*"
set "data=!data:*%1 %2 %3=!"
set "data=%data:~1%"
endlocal & set "%~2=%data%"
goto :eof
As npocmaka commented above, = has special meaning and cannot be replaced with traditional variable string manipulation. If you know the length of either side of the equal sign, you could strip off a number of characters. For example, if "4567" will always be 4 characters, you could set "var1=%str:~0,4%". Or if "abcde" will always be 5 characters, you could set "var1=%str:~0,-6%" (5 chars + 1 for the equal sign).
Otherwise, a for loop is your only other option without using 3rd party utilities.
for /f "delims==" %%I in ("%str%") do set "var1=%%I"
If you've got grep installed, you can do something like:
echo %str% | grep -P -o "^[^=]*"
... but you'd still need to capture its output with another for /f loop.
If you are allergic to for loops, and as an exercise in providing a solution to your question without any regard for efficiency, here's how you get the first half of your string without using a single for loop. Put grep and its dependencies in your %PATH%. Then:
echo %str% | grep -P -o "^[^=]*" >temp.txt
set /P "var1="<temp.txt
del temp.txt
echo %var1%
There, I fixed it!
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
)
Well, I am kinda stuck on the following part of my code.
This program will count the number of lines in the file usernames_list.txt. On each line there is a name of a folder I want to enter. I want that program to enter each folder in my list, create a file called test_1 and then go to it's parent folder. This should be reapeated until it reaches the end of the list.
Am I doing it right? :/
For some reason the "skip" option won't accept my variable.
for /f %%C in ('Find /V /C "" ^< usernames_list.txt') do set lines=%%C
set times=0
set /A skip_value=%lines%-(%lines%-%times%)
:redo
FOR /F "skip=%skip_value%" %%b IN (usernames_list.txt) DO (
cd %%b
echo > test_1
cd ..
set /A times=%times%+1
if /i {%times%}=={%lines%} (goto continue)
goto redo
)
:continue
pause
As currently written, set /A skip_value=%lines%-(%lines%-%times%) will always evaluate to 0 because times = 0.
The SKIP value must be >= 1. Setting SKIP=0 results in a syntax error.
It seems to me your logic for computing skip_value is flawed. But even if you fix the logic, you still have to worry about values that are <= 0. I handle that situation by defining a SKIP variable with the entire option text only if the value is >= 1.
set "skip="
if %skip_value% geq 1 set "skip=skip=%skip_value%"
for /f "%skip%" %%b in (usernames_list.txt) ...
You might need additional FOR /F options. That is not a problem. For example:
for /f "%skip% delims=" ...