How do I create HELP command for custom BAT files? - batch-file

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

Related

Is there a way to create a CMD command with arguments?

I'm trying to create a command in the command prompt, but use arguments like you can do with a function in another language.
For example, you can create a batch file in the C:\Windows\System32 folder and execute it in the command prompt by typing its name, creating a "command." This is very simple, though, and simply runs whatever the batch file contains.
My command I made is called toolbx, which has different useful shortcuts in the command prompt. But, I want to execute different tools by typing something like toolbx 1. Here's what it might look like.
toolbx [input received changing what tool you want to use] (e.g. 1)
In toolbx.bat
if %inputRecieved% == 1 goto 1
I'm not the best at explaining, so another example in Python might be:
def functionWithArgs (tool)
Executed as:
functionWithArgs(1)
As another summary:
I'm trying to make a command used in the command prompt with a batch file
I want the batch file to have arguments defined when the command is run (like the python example) that can be used as variables in the batch file.
Thank you in advance!
It is your lucky day today, windows batch files do in fact support a feature called command-line parameters. Here is what you need to do:
set inputRecieved=%1
if "%inputRecieved%"=="1" goto label1
goto :eof
:label1
blabla...
Have fun coding!
P.S.
Please note that the correct spelling of the word is "received", not "recieved".
You can use the macro style invented by #jeb, #dbenham and DosTips user #Ed Dyreen:
Definitions:
#echo off
SETLOCAL DISABLEDELAYEDEXPANSION
::Definitions
( set LF=^
%= EMPTY =%
)
set ^"NL=^^^%LF%%LF%^%LF%%LF%^^"
The whole purpose is to create a multiline variable. See the batch newline variable hack
Main Macro:
::Macro
ENDLOCAL &^
set $MACRO.Toolbx=FOR %%a in (args main) do if "%%a" == "main" (%NL%
for %%A in (%payload%) do (%NL%
%= MAIN MACRO HERE, END LINES WITH NL =%%NL%
)%NL%
) ELSE SETLOCAL ENABLEDELAYEDEXPANSION ^& set args=,
exit /b
Save this as MACRO.Toolbx.bat. To use the macro inside your batch file:
call MACRO.Toolbx.bat
%$MACRO.Toolbx% COMMAND_LINE_ARGS
The macro uses a clever FOR loop to "catch" the arguments. Also, in order to catch arguments, DISABLEDELAYEDEXPANSION is necessary, which is inseparable from SETLOCAL. However, ENDLOCAL will destroy all user-defined variable in that range, but we need the newline definitions. Therefore, ENDLOCAL and the macro definition must be on the same line. This technique is called ENDLOCAL tunneling.
sure
[Complete path and file name] [Parameter1] [Parameter2] [and_so_on]
Is a space that separates parameters that can be expanded with %1 %2 %3...
You can expand all parameters with %* or up to 9 parameters individually
for example:
%windir%\system32\toolbx.cmd Tool_1
note the parameter beign passed is: Tool_1
in the example below is beign used at line 2 where %1 expands: Tool_1
setlocal EnableDelayedExpansion
set tool=%1
set argument_1=Have
set argument_2=a
set argument_3=Nice
set argument_4=Day
CALL :Function_%tool% argument_1 argument_2 argument_3 argument_4
:: after Function_Tool_1 ends by reading the next GOTO :EOF after its Label,
:: will return to read instructions after this line
::
pause&&exit
::
:Function_Tool_1
echo. %argument_1% %argument_2% %argument_3% %argument_4%
goto :EOF
::
::
NOTE:
When using CALL to call labels, you have to use a colon for example:
CALL :YourLabel
And after the label all the parameters or arguments separated with a space
for example:
CALL :YourLabel param1 param2 param3 parampampam etc
I think the syntax for labels is the same as for variables.
You have to use a GOTO :EOF to end a function or procedure
See you don't use a colon when going to the label using GOTO for example:
GOTO YourLabel
but when using CALL is different, that is explained better typing:
EXIT /?
GOTO /?
& is used to separate instructions
&& is used to separate instructions inside a batch file as toolbx.cmd
Also note in the example above:
%1 %2 %3 and %4 along the function could expand the variable name, for example:
echo. %1 %2 %3 %4
would echo: argument_1 argument_2 argument_3 argument_4
and using SETLOCAL EnableDelayedExpansion
!%1! could expand the value of the variable, for example:
echo. !%1! !%2! !%3! !%4!
would echo: Have a Nice Day
A technique has been developed using for loops that allows arguments to be captured when a variable is expanded - Batch Macros.
An outer for loop is used, which on the first iteration assigns a variable containing the argument values, and the inner loop/s proccess and act upon those values.
#echo off
Set "Macroname=For %%n in (1 2)Do if %%n==2 (Echo(!Args!)Else Set args="
Setlocal EnableDelayedExpansion
%Macroname% Hello World
The technique requires defining the macro variable in an environment where Delayed expansion is disabled, then using Setlocal EnableDelayedExpansion to enable the macro. ( Unless of course all ! expansion symbols are correctly escaped during macro definition for an environment where delayed expansion is enabled. )
Below is a template for batch macro's that builds an Args array and and proccesses switches that trail arguments. The purpose of the template is to allow quick scripting of advanced functions by having all the argument and switch handling logic ready to go, so that the meat of the function is all that needs to be scripted.
Args are returned to the array $Macroname_args[!$Macroname_args[i]!] where:
!$Macroname_args[i]! is a 0 indexed count of args.
Switches are returned in associated variables using the switch string as the index IE:
%MacroName% /A
Results in: !$Macroname_Switch[A]!==true
Or if a subarg is supplied:
%MacroName% /A sub arg
Results in: !$Macroname_Switch[A]!==sub arg
The macro uses Substring modification to seperate switches from args and process switches.
As such, arg or switch values containing the following characters:
= * / "
should be supplied in a substituted form:
{EQ} {AS} {FS} {DQ}, which are replaced using substitution built into the macro.
#Echo off
(Set \n=^^^
%= \n macro newline variable. Do not modify =%)
(Set LF=^
%= LF newline variable. Do not modify =%)
:# Example Switches for demonstrating concept. List used twice within macro switch handling. Define outside macro to
:# simplify maintainance.
Set $MacroName_Switches="A" "B" "C" "D" "E" "Def"
Set $MacroName=For %%n in (1 2)Do if %%n==2 (%\n%
For /F "Tokens=1,2 Delims==" %%G in ('Set "$Macroname_Arg[" 2^^^> nul')Do Set "%%~G="%\n%
For %%G in ( %$MacroName_Switches% )Do Set "$MacroName_Switch[%%~G]="%\n%
If not "!$MacroName_args:* /=!" == "!$MacroName_args!" (%\n: build $MacroName.Args[!$MacroName_arg[i]!] array if args present =%
Set "$MacroName_leading.args=!$MacroName_args:*/=!"%\n%
For /F "Delims=" %%G in ("!$MacroName_leading.args!")Do Set "$MacroName_leading.args=!$MacroName_args:/%%G=!"%\n%
Set ^"$MacroName_args=!$MacroName_args:"=!"%\n%
Set "$MacroName_arg[i]=0"%\n%
For %%G in (!$MacroName_leading.args!)Do (%\n%
Set /A "$MacroName_arg[i]+=1"%\n%
Set "$MacroName_arg[!$MacroName_arg[i]!]=%%~G"%\n%
For %%i in ("!$MacroName_arg[i]!")Do (%\n%
Set "$MacroName_arg[%%~i]=!$MacroName_arg[%%~i]:{FS}=/!"%\n%
Set "$MacroName_arg[%%~i]=!$MacroName_arg[%%~i]:{AS}=*!"%\n%
Set "$MacroName_arg[%%~i]=!$MacroName_arg[%%~i]:{EQ}==!"%\n%
Set ^"$MacroName_arg[%%~i]=!$MacroName_arg[%%~i]:{DQ}="!"%\n%
)%\n%
)%\n%
) Else (%\n: remove doublequotes from args before switch processing switches =%
Set ^"$MacroName_args=!$MacroName_args:"=!"%\n%
)%\n%
Set "$MacroName_LastSwitch="%\n%
For /L %%L in (2 1 4)Do If "!$MacroName_LastSwitch!" == "" (%\n%
If "!$MacroName_Args:~-%%L,1!" == " " Set "$MacroName_LastSwitch=_"%\n%
If "!$MacroName_Args:~-%%L,1!" == "/" (%\n: Flag last switch true if no subargs; Works for character switches of up to 3 characters =%
For /F "Delims=" %%v in ('Set /A "%%L-1"')Do Set "$MacroName_Switch[!$MacroName_Args:~-%%v!]=true"%\n%
If not "!$MacroName_Args:/?=!." == "!$MacroName_Args!." Set "$MacroName_Switch[help]=true"%\n%
Set "$MacroName_Args=!$MacroName_Args:~0,-%%L!"%\n%
Set "$MacroName_LastSwitch=_"%\n%
)%\n%
)%\n%
For %%G in ( %$MacroName_Switches% )Do If not "!$MacroName_args:/%%~G =!" == "!$MacroName_args!" (%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Args:*/%%~G =!"%\n%
If not "!$MacroName_Switch[%%~G]:*/=!" == "!$MacroName_Switch[%%~G]!" (%\n%
Set "$MacroName_Trail[%%~G]=!$MacroName_Switch[%%~G]:*/=!"%\n%
For %%v in ("!$MacroName_Trail[%%~G]!")Do (%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]: /%%~v=!"%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:/%%~v=!"%\n%
)%\n%
Set "$MacroName_Trail[%%~G]="%\n%
If "!$MacroName_Switch[%%~G]:~-1!" == " " Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:~0,-1!"%\n%
If "!$MacroName_Switch[%%~G]!" == "" Set "$MacroName_Switch[%%~G]=true"%\n%
If not "!$MacroName_Switch[%%~G]!" == "" If not "!$MacroName_Switch[%%~G]!" == "true" (%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:{FS}=/!"%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:{AS}=*!"%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:{EQ}==!"%\n%
Set ^"$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:{DQ}="!"%\n%
)%\n%
)%\n%
)%\n: Insert Switch Assessment Below. Use conditional testing of valid switches to enact macro functions before $MacroName_args assignment =%
REM INSERT COMMANDS BETWEEN THE ABOVE AND BELOW LINES TERMINATING EACH WITH \n VARIABLE %\n%
%= EXAMPLE. Remove this line. =% Echo(!LF!Example: %%$Macroname%%!$Macroname_args!%\n%
%= EXAMPLE. Remove this line. =% Set $Macroname_Arg[%\n%
%= EXAMPLE. Remove this line. =% Set $Macroname_Switch[%\n%
%= ESCAPE AMPERSANDS AND REDIRECTION CHARACTERS DURING MACRO DEFINITION. =%) Else Set $MacroName_args=
Setlocal enableDelayedExpansion
:# Proof of concept
%$Macroname% "{AS} param 1 {EQ} {DQ}https:{FS}{FS}stackoverflow.com{FS}posts{FS}60793679{DQ}" "param 2" /C one /D 12 /Def
%$Macroname% /B a b /E /Def "hello world & ^! > < | %%" /A 1 + 2 {EQ} 3 /Undefined switch

Changing a flag in the ini file from batch script

I have a script which will check if the file names and the content of the files are same or not, below is the code and it is working fine
ECHO OFF
CLS
for %%i in (C:\Users\f1ym41a\Documents\deep\*.DAT) do (
fc C:\Users\f1ym41a\Documents\deep\MOVE.DAT %%i > NUL
if errorlevel 1 (
CALL :error
echo C:\Users\f1ym41a\Documents\deep\MOVE.DAT and %%i are different >>output.log
) ELSE (
CALL :next
echo C:\Users\f1ym41a\Documents\deep\MOVE.DAT and %%i are same >>output.log
)
timeout 5
)
PAUSE
What i need to do is if the file names are same then it will change the flag in the ini file to 1. Below is the ini file (deep.ini)
[INI]
flag = 0
Since i am new to batch scripting. Can somebody help me out with this?
You can try with replacer.bat:
call replacer.bat move.dat "flag = 0" "flag = 1"
This is an easy to achieve task with using JREPL.BAT written by Dave Benham which is a batch file / JScript hybrid to run a regular expression replace on a file using JScript.
#echo off
if not exist "%USERPROFILE%\Documents\deep\MOVE.DAT" goto :EOF
if not exist "%~dp0jrepl.bat" goto :EOF
call "%~dp0jrepl.bat" "^(flag *= *)0" "$11" /F "%USERPROFILE%\Documents\deep\MOVE.DAT" /O -
The batch file first checks if the file to modify exists at all and immediately exits if this condition is not true, see Where does GOTO :EOF return to?
The batch file JREPL.BAT must be stored in same directory as the batch file with the code above. For that reason the batch file checks next if JREPL.BAT really exists in directory of the batch file and exits if this condition is not true.
The meaning of the regular expression search string is:
^ ... find at beginning of a line
(...) ... a string referenced in replace string with $1 to keep this part of found string unmodified
flag ... case-sensitive the string flag
 * ... with 0 or more spaces
= ... and an equal sign
 * ... and once more 0 or more spaces
0 ... and the character 0.
The replace string back-references the found string between beginning of line and character 0 with $1 and replaces 0 by 1.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /? ... explains also %~dp0 ... drive and path of argument 0 which is the batch file path always ending with a backslash.
echo /?
goto /?
if /?
jrepl.bat /?
:: 1st need remove some possible space in the string to got more precision
when compare them: "flag = 0" will became "flag=0", no
space and no tab.
:: 2nd for to do this, use this "' (2 characters) to set a variable that
use "=" to string instead a special character,
by set "'=flag=0" (very old technical!)
:: 3rd treat equal, treat tab character, and to remove it, because some time
this is a invisible and possible character that can eventually occur
in file dat, see in this question 10878138
:: 4th Compare the strings by string from file by file, line by line...
:: finely You need replace line in the file (.dat or .ini) this part I´m really confuse, but the code are above, sorry if my error!
Obs: use the conversion of this "flag = 0" this this one "flag=0", only for processing comparatives operation, wend the %%i match flag = 0 then only changed to replace to files by flag = 1, bat, a specific thing here is the command fc are comparing %%i, by the same file in looping for with no other file.
#echo off && setlocal EnableExtensions EnableDelayedExpansion
set "'=flag=0"
set _file_new_flag1="%temp%\Flag1.dat"
set _path_to_dats=C:\Users\f1ym41a\Documents\deep\
for /f "delims= " %%T in ('forFiles /p "." /m "%~nx0" /c "cmd /c echo(0x09"') do set "_tab=%%T"
type nul >output.log && set "_tab=%_tab:~0,1%"
cd /d "!_path_to_dats!"
for /f "tokens=* delims= " %%x in ('dir /o-d /on /b "*.dat"') do (
if defined _file_new_flag (
move /y "!_file_new_flag1!" "!_file_now!"
set _file_now=<nul
set "_file_now=%%~x"
) else (
set "_file_now=%%~x"
)
call :_file_compare_:
)
endlocal & goto :_end_of_file_:
:_file_compare_:
for /f "tokens=* delims= " %%X in ('type "!_file_now!"') do (
for /f "tokens=* delims= " %%i in ('echo/"%%~X"') do (
set "_to_compare=%%~i"
call set "_to_compare=!_to_compare: =!"
for /f "tokens=* delims=" %%I in ('echo/%_tab%') do call set "_to_compare=!_to_compare:%%I=!"
if ["!_to_compare!"] equ ["%'%"] (
echo/C:\Users\f1ym41a\Documents\deep\MOVE.DAT and %%i are same >>output.log
echo/%%~i>>!_file_new_flag1!
) else (
echo/C:\Users\f1ym41a\Documents\deep\MOVE.DAT and %%i are different >>output.log
echo/flag = 1>>!_file_new_flag1!
)
timeout /t 5
set _to_compare=<nul
)
)
exit /b
:_end_of_file_:
So sorry about my English.

Replace the string from output

I have file C:/test.txt which is having content as below.
05/13/2017 07:29:34 Value= \\america.com\efpf_share\efpf\ipm_files
05/13/2017 07:29:41 Value= \\america.com\efpf_share\efpf\ipm_files
05/17/2017 08:31:54 Value= \\america.com\efpf_share\efpf\ipm_files
05/17/2017 08:32:03 Value= \\america.com\efpf_share\efpf\ipm_files
I want to extract 'epfp' or any string comes at this place and convert this into upaercase also if its have test attached (as epfptest) then it it should split EPFP-TEST. For extracting I am running the below code and redirecting the output in temp1.txt file
findstr "Value=" C:\test.txt| findstr america > "C:\temp.txt" && for /l %l in (1,1,1) do #for /f "tokens=3* delims=." %a in ('findstr /n /r "^" "C:\temp.txt" ^| findstr /r "^%l:"') do #echo %b > c:\temp1.txt
now temp1.txt file having the content as below :
com\efpf_share\efpf\ipm_files
Now finally I am exracting efpf from below code it gives me the output as below :
for /f "tokens=3 delims=\" %a in (c:\temp1.txt) do #echo %a
epfp
I want this output or to be converted as EPFP (in uppercare) and if this output does not having test string attached then it should only split as EPFP-TEST
Note: Final output can be anything (in this case epfp) and I want this convert in uppercase also if this output containing 'test' string attached then that should be split in "STRING-TEST"
This test file modification task should be definitely not done using a batch file and pure Windows command processor commands. There are much better scripting languages for this task.
It would be also much more useful to do this file content modification with a powerful text editor like UltraEdit or any other text editor with Perl regular expression support. Searching for (\\[^\\]+\\)(?=ipm_files) and using as replace string \U$1\E changes the directory name left to ipm_files to upper case and searching for (?<!\\|-)TEST(?=\\ipm_files) and using as replace string -TEST inserts the hyphen character left to TEST if there is not already a hyphen and the entire folder name is not TEST.
However, here is a commented batch file solution for this task:
#echo off
if not exist "%~dp0Test.txt" goto :EOF
setlocal EnableExtensions DisableDelayedExpansion
set "Modified=0"
set "DataFile=%~dp0Test.txt"
set "TempFile=%TEMP%\%~n0.tmp"
del "%TempFile%" 2>nul
for /F delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /N "^" "%DataFile%" 2^>nul') do (
set "Line=%%I"
call :ProcessLine
)
if %Modified% == 1 move /Y "%TempFile%" "%DataFile%"
del "%TempFile%" 2>nul
endlocal
goto :EOF
rem The subroutine ProcessLine removes first line number and colon inserted
rem by FINDSTR at beginning of each line to process correct also empty lines
rem in data file. The subroutine jumps to output of line in case of current
rem line is an empty line.
rem Next the line is split up into substrings using backslash as delimiter.
rem Of interest are only the fourth and fifth substrings. The fifth substring
rem should be ipm_files to identify the current line as a line to process.
rem A jump to writing the line into temporary file is done if this condition
rem is not true. Otherwise the fourth substring is assigned to a variable
rem because that string is the folder name to modify by this batch file.
rem Each ASCII character in the folder name is replaced by its upper case character.
rem If the entire new folder name is TEST, just do the replace and don't change
rem the folder name to -TEST. If the new folder name ends already with -TEST,
rem just do the replace. But if new folder name ends with only TEST, replace
rem just TEST by -TEST with hyphen.
rem A case-sensitive comparison of current and new folder name is done before
rem running the folder replace on line to determine if the replace is really
rem necessary at all. The modification information is saved in an environment
rem variable which is passed over local environment of subroutine to main code
rem above. This information is used finally to determine if the data file must
rem be replaced at all by the temporary file because of a modification is made
rem or the temporary file can be simply deleted as being equal with data file.
:ProcessLine
setlocal EnableDelayedExpansion
set "Line=!Line:*:=!"
if not defined Line goto WriteLine
for /F "tokens=4,5 delims=\" %%A in ("!Line!") do (
if /I not "%%B" == "ipm_files" goto WriteLine
set "CurFolderName=%%A"
)
set "NewFolderName=%CurFolderName%"
for %%C in (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) do set "NewFolderName=!NewFolderName:%%C=%%C!"
if "%NewFolderName%" == "TEST" goto DoReplace
if "%NewFolderName:~-5%" == "-TEST" goto DoReplace
if "%NewFolderName:~-4%" == "TEST" set "NewFolderName=%NewFolderName:~0,-4%-TEST"
:DoReplace
if "%CurFolderName%" == "%NewFolderName%" goto WriteLine
set "Modified=1"
set "Line=!Line:%CurFolderName%\ipm_files=%NewFolderName%\ipm_files!"
:WriteLine
echo(!Line!>>"%TempFile%"
endlocal & set "Modified=%Modified%"
goto :EOF
%~dp0Test.txt must be two times replaced by real file name of data file with relative or absolute path.
The purpose of first FOR loop in main code at top is described in my answer on:
How to read and print contents of text file line by line?
The other command lines are explained by the remarks between main code and subroutine.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
del /?
echo /?
endlocal /?
findstr /?
goto /?
if /?
move /?
rem /?
setlocal /?

Building variable in batch file from FOR loop

I would like to build a string for use as an argument in a command line utility. The string will be built from the presence of certain services which I have defined in a variable. I have the following code so far:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "PRODS=Service1 Service2 Service3"
REM Let's see what's installed.
SET NEWPRODS=
FOR %%i in (%PRODS%) DO (
%comspec% /c %WINDIR%\system32\sc.exe query %%i | %WINDIR%\system32\findstr /C:"RUNNING">nul
IF NOT ERRORLEVEL 1 (
SET NEWPRODS=%%i
ECHO !NEWPRODS!
)
)
How do I make the output (let's assume that service 1 and 3 is found and is running) to be like so: Service1,Service3?
Thanks in advance.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "PRODS=Service1 Service2 Service3"
REM Let's see what's installed.
SET "NEWPRODS="
FOR %%i in (%PRODS%) DO sc.exe query %%i | findstr /C:"RUNNING">nul && SET "NEWPRODS=!NEWPRODS!%%i,"
REM Trim trailing comma if string is not empty
IF NOT "%NEWPRODS%"=="" SET "NEWPRODS=%NEWPRODS:~0,-1%"
ECHO(%NEWPRODS%
pause
Don't do
echo %SomeVariable%
If the variable is blank it will display the current ECHO state.
Use quotes when setting strings so you don't get any unintended spaces.

How can I pass arguments to a batch file?

I need to pass an ID and a password to a batch file at the time of running rather than hardcoding them into the file.
Here's what the command line looks like:
test.cmd admin P#55w0rd > test-log.txt
Another useful tip is to use %* to mean "all". For example:
echo off
set arg1=%1
set arg2=%2
shift
shift
fake-command /u %arg1% /p %arg2% %*
When you run:
test-command admin password foo bar
The above batch file will run:
fake-command /u admin /p password admin password foo bar
I may have the syntax slightly wrong, but this is the general idea.
Here's how I did it:
#fake-command /u %1 /p %2
Here's what the command looks like:
test.cmd admin P#55w0rd > test-log.txt
The %1 applies to the first parameter the %2 (and here's the tricky part) applies to the second. You can have up to 9 parameters passed in this way.
If you want to intelligently handle missing parameters you can do something like:
IF %1.==. GOTO No1
IF %2.==. GOTO No2
... do stuff...
GOTO End1
:No1
ECHO No param 1
GOTO End1
:No2
ECHO No param 2
GOTO End1
:End1
Accessing batch parameters can be simple with %1, %2, ... %9 or also %*,
but only if the content is simple.
There is no simple way for complex contents like "&"^&, as it's not possible to access %1 without producing an error.
set var=%1
set "var=%1"
set var=%~1
set "var=%~1"
The lines expand to
set var="&"&
set "var="&"&"
set var="&"&
set "var="&"&"
And each line fails, as one of the & is outside of the quotes.
It can be solved with reading from a temporary file a remarked version of the parameter.
#echo off
SETLOCAL DisableDelayedExpansion
SETLOCAL
for %%a in (1) do (
set "prompt="
echo on
for %%b in (1) do rem * #%1#
#echo off
) > param.txt
ENDLOCAL
for /F "delims=" %%L in (param.txt) do (
set "param1=%%L"
)
SETLOCAL EnableDelayedExpansion
set "param1=!param1:*#=!"
set "param1=!param1:~0,-2!"
echo %%1 is '!param1!'
The trick is to enable echo on and expand the %1 after a rem statement (works also with %2 .. %*).
So even "&"& could be echoed without producing an error, as it is remarked.
But to be able to redirect the output of the echo on, you need the two for-loops.
The extra characters * # are used to be safe against contents like /? (would show the help for REM).
Or a caret ^ at the line end could work as a multiline character, even in after a rem.
Then reading the rem parameter output from the file, but carefully.
The FOR /F should work with delayed expansion off, else contents with "!" would be destroyed.
After removing the extra characters in param1, you got it.
And to use param1 in a safe way, enable the delayed expansion.
Yep, and just don't forget to use variables like %%1 when using if and for and the gang.
If you forget the double %, then you will be substituting in (possibly null) command line arguments and you will receive some pretty confusing error messages.
A friend was asking me about this subject recently, so I thought I'd post how I handle command-line arguments in batch files.
This technique has a bit of overhead as you'll see, but it makes my batch files very easy to understand and quick to implement. As well as supporting the following structures:
>template.bat [-f] [--flag] [--namedvalue value] arg1 [arg2][arg3][...]
The jist of it is having the :init, :parse, and :main functions.
Example usage
>template.bat /?
test v1.23
This is a sample batch file template,
providing command-line arguments and flags.
USAGE:
test.bat [flags] "required argument" "optional argument"
/?, --help shows this help
/v, --version shows the version
/e, --verbose shows detailed output
-f, --flag value specifies a named parameter value
>template.bat <- throws missing argument error
(same as /?, plus..)
**** ****
**** MISSING "REQUIRED ARGUMENT" ****
**** ****
>template.bat -v
1.23
>template.bat --version
test v1.23
This is a sample batch file template,
providing command-line arguments and flags.
>template.bat -e arg1
**** DEBUG IS ON
UnNamedArgument: "arg1"
UnNamedOptionalArg: not provided
NamedFlag: not provided
>template.bat --flag "my flag" arg1 arg2
UnNamedArgument: "arg1"
UnNamedOptionalArg: "arg2"
NamedFlag: "my flag"
>template.bat --verbose "argument #1" --flag "my flag" second
**** DEBUG IS ON
UnNamedArgument: "argument #1"
UnNamedOptionalArg: "second"
NamedFlag: "my flag"
template.bat
#::!/dos/rocks
#echo off
goto :init
:header
echo %__NAME% v%__VERSION%
echo This is a sample batch file template,
echo providing command-line arguments and flags.
echo.
goto :eof
:usage
echo USAGE:
echo %__BAT_NAME% [flags] "required argument" "optional argument"
echo.
echo. /?, --help shows this help
echo. /v, --version shows the version
echo. /e, --verbose shows detailed output
echo. -f, --flag value specifies a named parameter value
goto :eof
:version
if "%~1"=="full" call :header & goto :eof
echo %__VERSION%
goto :eof
:missing_argument
call :header
call :usage
echo.
echo **** ****
echo **** MISSING "REQUIRED ARGUMENT" ****
echo **** ****
echo.
goto :eof
:init
set "__NAME=%~n0"
set "__VERSION=1.23"
set "__YEAR=2017"
set "__BAT_FILE=%~0"
set "__BAT_PATH=%~dp0"
set "__BAT_NAME=%~nx0"
set "OptHelp="
set "OptVersion="
set "OptVerbose="
set "UnNamedArgument="
set "UnNamedOptionalArg="
set "NamedFlag="
:parse
if "%~1"=="" goto :validate
if /i "%~1"=="/?" call :header & call :usage "%~2" & goto :end
if /i "%~1"=="-?" call :header & call :usage "%~2" & goto :end
if /i "%~1"=="--help" call :header & call :usage "%~2" & goto :end
if /i "%~1"=="/v" call :version & goto :end
if /i "%~1"=="-v" call :version & goto :end
if /i "%~1"=="--version" call :version full & goto :end
if /i "%~1"=="/e" set "OptVerbose=yes" & shift & goto :parse
if /i "%~1"=="-e" set "OptVerbose=yes" & shift & goto :parse
if /i "%~1"=="--verbose" set "OptVerbose=yes" & shift & goto :parse
if /i "%~1"=="--flag" set "NamedFlag=%~2" & shift & shift & goto :parse
if /i "%~1"=="-f" set "NamedFlag=%~2" & shift & shift & goto :parse
if not defined UnNamedArgument set "UnNamedArgument=%~1" & shift & goto :parse
if not defined UnNamedOptionalArg set "UnNamedOptionalArg=%~1" & shift & goto :parse
shift
goto :parse
:validate
if not defined UnNamedArgument call :missing_argument & goto :end
:main
if defined OptVerbose (
echo **** DEBUG IS ON
)
echo UnNamedArgument: "%UnNamedArgument%"
if defined UnNamedOptionalArg echo UnNamedOptionalArg: "%UnNamedOptionalArg%"
if not defined UnNamedOptionalArg echo UnNamedOptionalArg: not provided
if defined NamedFlag echo NamedFlag: "%NamedFlag%"
if not defined NamedFlag echo NamedFlag: not provided
:end
call :cleanup
exit /B
:cleanup
REM The cleanup function is only really necessary if you
REM are _not_ using SETLOCAL.
set "__NAME="
set "__VERSION="
set "__YEAR="
set "__BAT_FILE="
set "__BAT_PATH="
set "__BAT_NAME="
set "OptHelp="
set "OptVersion="
set "OptVerbose="
set "UnNamedArgument="
set "UnNamedArgument2="
set "NamedFlag="
goto :eof
There is no need to complicate it. It is simply command %1 %2 parameters, for example,
#echo off
xcopy %1 %2 /D /E /C /Q /H /R /K /Y /Z
echo copied %1 to %2
pause
The "pause" displays what the batch file has done and waits for you to hit the ANY key. Save that as xx.bat in the Windows folder.
To use it, type, for example:
xx c:\f\30\*.* f:\sites\30
This batch file takes care of all the necessary parameters, like copying only files, that are newer, etc. I have used it since before Windows. If you like seeing the names of the files, as they are being copied, leave out the Q parameter.
In batch file
set argument1=%1
set argument2=%2
echo %argument1%
echo %argument2%
%1 and %2 return the first and second argument values respectively.
And in command line, pass the argument
Directory> batchFileName admin P#55w0rd
Output will be
admin
P#55w0rd
#ECHO OFF
:Loop
IF "%1"=="" GOTO Continue
SHIFT
GOTO Loop
:Continue
Note: IF "%1"=="" will cause problems if %1 is enclosed in quotes itself.
In that case, use IF [%1]==[] or, in NT 4 (SP6) and later only, IF "%~1"=="" instead.
Everyone has answered with really complex responses, however it is actually really simple. %1 %2 %3 and so on are the arguements parsed to the file. %1 is arguement 1, %2 is arguement 2 and so on.
So, if I have a bat script containing this:
#echo off
echo %1
and when I run the batch script, I type in this:
C:> script.bat Hello
The script will simply output this:
Hello
This can be very useful for certain variables in a script, such as a name and age. So, if I have a script like this:
#echo off
echo Your name is: %1
echo Your age is: %2
When I type in this:
C:> script.bat Oliver 1000
I get the output of this:
Your name is: Oliver
Your age is: 1000
Let's keep this simple.
Here is the .cmd file.
#echo off
rem this file is named echo_3params.cmd
echo %1
echo %2
echo %3
set v1=%1
set v2=%2
set v3=%3
echo v1 equals %v1%
echo v2 equals %v2%
echo v3 equals %v3%
Here are 3 calls from the command line.
C:\Users\joeco>echo_3params 1abc 2 def 3 ghi
1abc
2
def
v1 equals 1abc
v2 equals 2
v3 equals def
C:\Users\joeco>echo_3params 1abc "2 def" "3 ghi"
1abc
"2 def"
"3 ghi"
v1 equals 1abc
v2 equals "2 def"
v3 equals "3 ghi"
C:\Users\joeco>echo_3params 1abc '2 def' "3 ghi"
1abc
'2
def'
v1 equals 1abc
v2 equals '2
v3 equals def'
C:\Users\joeco>
FOR %%A IN (%*) DO (
REM Now your batch file handles %%A instead of %1
REM No need to use SHIFT anymore.
ECHO %%A
)
This loops over the batch parameters (%*) either they are quoted or not, then echos each parameter.
I wrote a simple read_params script that can be called as a function (or external .bat) and will put all variables into the current environment. It won't modify the original parameters because the function is being called with a copy of the original parameters.
For example, given the following command:
myscript.bat some -random=43 extra -greeting="hello world" fluff
myscript.bat would be able to use the variables after calling the function:
call :read_params %*
echo %random%
echo %greeting%
Here's the function:
:read_params
if not %1/==/ (
if not "%__var%"=="" (
if not "%__var:~0,1%"=="-" (
endlocal
goto read_params
)
endlocal & set %__var:~1%=%~1
) else (
setlocal & set __var=%~1
)
shift
goto read_params
)
exit /B
Limitations
Cannot load arguments with no value such as -force. You could use -force=true but I can't think of a way to allow blank values without knowing a list of parameters ahead of time that won't have a value.
Changelog
2/18/2016
No longer requires delayed expansion
Now works with other command line arguments by looking for - before parameters.
Inspired by an answer elsewhere by #Jon, I have crafted a more general algorithm for extracting named parameters, optional values, and switches.
Let us say that we want to implement a utility foobar. It requires an initial command. It has an optional parameter --foo which takes an optional value (which cannot be another parameter, of course); if the value is missing it defaults to default. It also has an optional parameter --bar which takes a required value. Lastly it can take a flag --baz with no value allowed. Oh, and these parameters can come in any order.
In other words, it looks like this:
foobar <command> [--foo [<fooval>]] [--bar <barval>] [--baz]
Here is a solution:
#ECHO OFF
SETLOCAL
REM FooBar parameter demo
REM By Garret Wilson
SET CMD=%~1
IF "%CMD%" == "" (
GOTO usage
)
SET FOO=
SET DEFAULT_FOO=default
SET BAR=
SET BAZ=
SHIFT
:args
SET PARAM=%~1
SET ARG=%~2
IF "%PARAM%" == "--foo" (
SHIFT
IF NOT "%ARG%" == "" (
IF NOT "%ARG:~0,2%" == "--" (
SET FOO=%ARG%
SHIFT
) ELSE (
SET FOO=%DEFAULT_FOO%
)
) ELSE (
SET FOO=%DEFAULT_FOO%
)
) ELSE IF "%PARAM%" == "--bar" (
SHIFT
IF NOT "%ARG%" == "" (
SET BAR=%ARG%
SHIFT
) ELSE (
ECHO Missing bar value. 1>&2
ECHO:
GOTO usage
)
) ELSE IF "%PARAM%" == "--baz" (
SHIFT
SET BAZ=true
) ELSE IF "%PARAM%" == "" (
GOTO endargs
) ELSE (
ECHO Unrecognized option %1. 1>&2
ECHO:
GOTO usage
)
GOTO args
:endargs
ECHO Command: %CMD%
IF NOT "%FOO%" == "" (
ECHO Foo: %FOO%
)
IF NOT "%BAR%" == "" (
ECHO Bar: %BAR%
)
IF "%BAZ%" == "true" (
ECHO Baz
)
REM TODO do something with FOO, BAR, and/or BAZ
GOTO :eof
:usage
ECHO FooBar
ECHO Usage: foobar ^<command^> [--foo [^<fooval^>]] [--bar ^<barval^>] [--baz]
EXIT /B 1
Use SETLOCAL so that the variables don't escape into the calling environment.
Don't forget to initialize the variables SET FOO=, etc. in case someone defined them in the calling environment.
Use %~1 to remove quotes.
Use IF "%ARG%" == "" and not IF [%ARG%] == [] because [ and ] don't play will at all with values ending in a space.
Even if you SHIFT inside an IF block, the current args such as %~1 don't get updated because they are determined when the IF is parsed. You could use %~1 and %~2 inside the IF block, but it would be confusing because you had a SHIFT. You could put the SHIFT at the end of the block for clarity, but that might get lost and/or confuse people as well. So "capturing" %~1 and %~1 outside the block seems best.
You don't want to use a parameter in place of another parameter's optional value, so you have to check IF NOT "%ARG:~0,2%" == "--".
Be careful only to SHIFT when you use one of the parameters.
The duplicate code SET FOO=%DEFAULT_FOO% is regrettable, but the alternative would be to add an IF "%FOO%" == "" SET FOO=%DEFAULT_FOO% outside the IF NOT "%ARG%" == "" block. However because this is still inside the IF "%PARAM%" == "--foo" block, the %FOO% value would have been evaluated and set before you ever entered the block, so you would never detect that both the --foo parameter was present and also that the %FOO% value was missing.
Note that ECHO Missing bar value. 1>&2 sends the error message to stderr.
Want a blank line in a Windows batch file? You gotta use ECHO: or one of the variations.
To refer to a set variable in command line you would need to use %a% so for example:
set a=100
echo %a%
rem output = 100
Note: This works for Windows 7 pro.
For to use looping get all arguments and in pure batch:
Obs: For using without: ?*&|<>
#echo off && setlocal EnableDelayedExpansion
for %%Z in (%*)do set "_arg_=%%Z" && set/a "_cnt+=1+0" && (
call set "_arg_[!_cnt!]=!_arg_!" && for /l %%l in (!_cnt! 1 !_cnt!
)do echo/ The argument n:%%l is: !_arg_[%%l]!
)
goto :eof
Your code is ready to do something with the argument number where it needs, like...
#echo off && setlocal EnableDelayedExpansion
for %%Z in (%*)do set "_arg_=%%Z" && set/a "_cnt+=1+0" && call set "_arg_[!_cnt!]=!_arg_!"
fake-command /u !_arg_[1]! /p !_arg_[2]! > test-log.txt
Simple solution(even though question is old)
Test1.bat
echo off
echo "Batch started"
set arg1=%1
echo "arg1 is %arg1%"
echo on
pause
CallTest1.bat
call "C:\Temp\Test1.bat" pass123
output
YourLocalPath>call "C:\Temp\test.bat" pass123
YourLocalPath>echo off
"Batch started"
"arg1 is pass123"
YourLocalPath>pause
Press any key to continue . . .
Where YourLocalPath is current directory path.
To keep things simple store the command param in variable and use variable for comparison.
Its not just simple to write but its simple to maintain as well so if later some other person or you read your script after long period of time, it will be easy to understand and maintain.
To write code inline : see other answers.
Make a new batch file (example: openclass.bat) and write this line in the file:
java %~n1
Then place the batch file in, let's say, the system32 folder, go to your Java class file, right click, Properties, Open with..., then find your batch file, select it and that's that...
It works for me.
PS: I can't find a way to close the cmd window when I close the Java class. For now...
Paired arguments
If you prefer passing the arguments in a key-value pair you can use something like this:
#echo off
setlocal enableDelayedExpansion
::::: asigning arguments as a key-value pairs:::::::::::::
set counter=0
for %%# in (%*) do (
set /a counter=counter+1
set /a even=counter%%2
if !even! == 0 (
echo setting !prev! to %%#
set "!prev!=%%~#"
)
set "prev=%%~#"
)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: showing the assignments
echo %one% %two% %three% %four% %five%
endlocal
And an example :
c:>argumentsDemo.bat one 1 "two" 2 three 3 four 4 "five" 5
1 2 3 4 5
Predefined variables
You can also set some environment variables in advance. It can be done by setting them in the console or setting them from my computer:
#echo off
if defined variable1 (
echo %variable1%
)
if defined variable2 (
echo %variable2%
)
and calling it like:
c:\>set variable1=1
c:\>set variable2=2
c:\>argumentsTest.bat
1
2
File with listed values
You can also point to a file where the needed values are preset.
If this is the script:
#echo off
setlocal
::::::::::
set "VALUES_FILE=E:\scripts\values.txt"
:::::::::::
for /f "usebackq eol=: tokens=* delims=" %%# in ("%VALUES_FILE%") do set "%%#"
echo %key1% %key2% %some_other_key%
endlocal
and values file is this:
:::: use EOL=: in the FOR loop to use it as a comment
key1=value1
key2=value2
:::: do not left spaces arround the =
:::: or at the begining of the line
some_other_key=something else
and_one_more=more
the output of calling it will be:
value1 value2 something else
Of course you can combine all approaches. Check also arguments syntax , shift
If you're worried about security/password theft (that led you to design this solution that takes login credentials at execution instead of static hard coding without the need for a database), then you could store the api or half the code of password decryption or decryption key in the program file, so at run time, user would type username/password in console to be hashed/decrypted before passed to program code for execution via set /p, if you're looking at user entering credentials at run time.
If you're running a script to run your program with various user/password, then command line args will suit you.
If you're making a test file to see the output/effects of different logins, then you could store all the logins in an encrypted file, to be passed as arg to test.cmd, unless you wanna sit at command line & type all the logins until finished.
The number of args that can be supplied is limited to total characters on command line. To overcome this limitation, the previous paragraph trick is a workaround without risking exposure of user passwords.

Resources