I have written a batch file that I use for file management. The batch file parses an .XML database to get a list of base filenames, then allows the user to move/copy those specific files into a new directory. The program prompts the user for a source directory and the name of the .XML file. I would like the program to default the variables to the last used entry, even if the previous CMD session has closed. My solution has been to ask the user for each variable at the beginning of the program, then write those variables to a separate batch file called param.bat at the end like this:
#echo off
set SOURCEDIR=NOT SET
set XMLFILE=NOT SET
if exist param.bat call param.bat
set /p SOURCEDIR=The current source directory is %SOURCEDIR%. Please input new directory or press [Enter] for no change.
set /p XMLFILE=The current XML database is %XMLFILE%. Please input new database or press [Enter] for no change.
REM {Rest of program goes here}
echo #echo off>param.bat
echo set SOURCEDIR=%SOURCEDIR%>>param.bat
echo set XMLFILE=%XMLFILE%>>param.bat
:END
I was hoping for a more elegant solution that does not require a separate batch file and allows me to store the variable data within the primary batch file itself. Any thoughts?
#echo off
setlocal
dir /r "%~f0" | findstr /c:" %~nx0:settings" 2>nul >nul && (
for /f "usebackq delims=" %%A in ("%~f0:settings") do set %%A
)
if defined SOURCEDIR echo The current source directory is %SOURCEDIR%.
set /p "SOURCEDIR= Please input new directory or press [Enter] for no change. "
if defined XMLFILE echo The current XML database is %XMLFILE%.
set /p "XMLFILE=Please input new database or press [Enter] for no change. "
(
echo SOURCEDIR=%SOURCEDIR%
echo XMLFILE=%XMLFILE%
) > "%~f0:settings"
This uses the Alternate Data Stream (ADS) of the batchfile to
save the settings.
NTFS file system is required. The ADS stream is lost if the
batchfile is copied to a file system other than NTFS.
The dir piped to findstr is to determine if the
stream does exist before trying to read from it.
This helps to avoid an error message from the for
loop if the ADS does not exist.
The for loop sets the variable names and values read from the ADS.
Finally, the variables are saved to the ADS.
Note:
%~f0 is full path to the batchfile.
See for /? about all modifiers available.
%~f0:settings is the batchfile with ADS named settings.
dir /r displays files and those with ADS.
Important:
Any idea involving writing to the batchfile could result
in file corruption so would certainly advise a backup of
the batchfile.
There is one way to save variables itself on the bat file, but, you need replace :END to :EOF
:EOF have a good explained in this link .:|:. see Where does GOTO :EOF return to?
Also, this work in fat32/ntfs file system!
You can write the variables in your bat file, and read when needs:
Obs.: Sorry my limited English
#echo off & setlocal enabledelayedexpansion
set "bat_file="%temp%\new_bat_with_new_var.tmp"" & type nul >!bat_file! & set "nop=ot Se"
for /f %%a in ('forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo 0x40"') do set "delim=%%a"
type "%~f0"| findstr "!delim!"| find /v /i "echo" >nul || for %%s in (SOURCEDIR XMLFILE) do set "%%s=N!nop!t"
if defined SOURCEDIR echo/!SOURCEDIR!%delim%!XMLFILE!%delim%>>"%~f0"
for /f "delims=%delim% tokens=1,2" %%a in ('type "%~f0"^| findstr /l "!delim!"^| find /v /i "echo"') do (
set /p "SOURCEDIR=The current source directory is %%~a. Please input new directory or press [Enter] for no change: "
set /p "XMLFILE=The current XML database is %%~b. Please input new database or press [Enter] for no change: "
if /i "!old_string!" neq "!SOURCEDIR!!delim!!XMLFILE!!delim!" (
type "%~f0"| findstr /vic:"%%~a!delim!%%~b!delim!">>!bat_file!"
copy /y !bat_file! "%~f0" >nul
echo/!SOURCEDIR!!delim!!XMLFILE!!delim!>>%~f0"
goto :_continue_:
))
:_continue_:
rem :| Rest of program goes here | replace/change last command [goto :END] to [goto :EOF]
goto :EOF
rem :| Left 2 line blank above, because your variable will be save/read in next line above here |:
Okay here is what I got so far.
This is meant to add websites to block in the hosts file, as well as allow the user to delete the entries when they want to. When trying to add a website to block sometimes it creates a new line then puts the entry on the line before it. This is not what I want. I want it to create a new line then add the entry on that line. For some reason it works sometimes and other times it don't work at all. I get an error message that says Find parameter is incorrect. I am using the Find command to see if the entries is already in the hosts file. If it is I want it to avoid adding it. If it is not then I want to add it. When I try to delete a entry the batch just crashes, so I am not really sure what I am doing wrong here. I am trying to find the entry and replace it with nothing. What I really want to do is delete the entire line so that I don't end up with a lot of blank lines.
Any help is greatly appreciated. Thanks!
#echo off
TITLE Modifying your HOSTS file
COLOR F0
:LOOP
cls
SET "CHOICE="
ECHO Choose 1 to block a website
ECHO Choose 2 remove a blocked website
ECHO Choose 3 to exit
SET /P CHOICE= selection %CHOICE%
GOTO %CHOICE%
:1
cls
SET /P WEBSITE=Enter the name of the website to add:
SET HOME= 127.0.0.1
SET NEWLINE=^& echo.
SET BLOCKEDSITE=%HOME% %WEBSITE%
FIND /C /I %BLOCKEDSITE% %WINDIR%\system32\drivers\etc\hosts
IF %ERRORLEVEL% NEQ 0 ECHO %NEWLINE%^%BLOCKEDSITE%>>%WINDIR%\System32\drivers\etc\hosts
ECHO Website blocked
ping 127.0.0.1 -n 5 > nul
GOTO LOOP
:2
cls
SET /P WEBSITE=Enter the name of the website to remove:
SETLOCAL ENABLEEXTENTIONS DISABLEDELAYEDEXPANSION
SET "HOME= 127.0.0.1 "
SET "BLOCKEDSITE=%HOME% %WEBSITE%"
SET "REPLACE="
SET "HOSTSFILE=%WINDIR%\system32\drivers\etc\hosts"
FOR /F "DELIMS=" %%i IN ('TYPE "%HOSTSFILE%" ^& BREAK ^> "%HOSTSFILE%" ')
DO
(
SET "LINE=%%i"
SETLOCAL ENABLEDELAYEDEXPANSION
>>"%HOSTSFILE%" echo(!LINE:%BLOCKEDSITE%=%REPLACE%!
ENDLOCAL
)
ECHO Website unblocked
GOTO LOOP
:3
EXIT
Please note that the term website is misleading when referring to the entries of the hosts file. The entries of hosts file are used for custom mappings of DNS host names to IP addresses, and any host name that is present in the file does not necessarily hosts a website. Using the term website may lead to the false impression that something like http://www.example.com can be added to hosts file which is not true.
Skipping a host if it is already present in the hosts file:
The problem with your usage of find is that %BLOCKEDSITE% has embedded spaces so you should enclose it quotes and use:
FIND /C /I "%BLOCKEDSITE%" %WINDIR%\system32\drivers\etc\hosts
But it has another problem: Because of its dependency on the exact spacing between the IP address and host name which is mandated by %BLOCKEDSITE% It only works for the entries that are added by your batch file. Additionally the user may have commented out (disabled) an entry by placing # in the begging of the line that contains the entry, and your batch code will skip adding the host even if the entry is disabled.
This can be resolved by using findstr with its regex syntax. for example:
findstr /IRC:"^ *127\.0\.0\.1 *example\.com *$" "%WINDIR%\system32\drivers\etc\hosts"
Removing an entry from the hosts file:
In the FOR loop you just have to skip writing the lines that contains the specified entry:
if "!Line!"=="!LINE:%BLOCKEDSITE%=!" echo(!Line!>>"%HOSTSFILE%"
But again it is not accurate and is suffering from the same problems that are mentioned earlier for skipping adding the entry. Again By using findstr you can easily remove the lines that contain the unwanted entry:
findstr /VIRC:"^ *127\.0\.0\.1 *example\.com *$" "%HOSTSFILE%" > "%HOSTSFILE%.tmp"
del "%HOSTSFILE%"
ren "%HOSTSFILE%.tmp" "hosts"
With above mentioned points the script can be rewritten like this:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
title Modifying your HOSTS file
color F0
set "HOSTSFILE=%WINDIR%\system32\drivers\etc\hosts"
set "HOME=127.0.0.1"
set "PROMPT_TEXT=Enter the host name to"
set "ACTION_TEXT[1]=add"
set "ACTION_TEXT[2]=remove"
set "FindEmptyLine=^ *$"
set "NewLineAppended="
cls
setlocal EnableDelayedExpansion
:LOOP
echo,
echo 1. Block a host
echo 2. Remove a blocked host
echo 3. Exit
choice /C "123" /N /M "Choose an item [1, 2, 3]: "
set "Item=%errorlevel%"
goto choice%Item%
:choice0 // User Pressed CTRL-C
:choice3
exit /b
:choice1
call :Common
set "HostEntry=!HOME! !HOST!"
findstr /IRC:"!FindEntry!" "!HOSTSFILE!"> nul && (
echo The host !HOST! is already blocked, No action taken.
) || (
if not defined NewLineAppended (
REM This will append a new line ONLY if the file does not end by LF character
type "!HOSTSFILE!" | findstr $ > "!HOSTSFILE!.tmp" && (
del "!HOSTSFILE!"
ren "!HOSTSFILE!.tmp" "hosts"
set "NewLineAppended=1"
)
)
echo !HostEntry!>>"!HOSTSFILE!"
echo The host !HOST! blocked
)
goto LOOP
:choice2
call :Common
findstr /VIR /C:"!FindEntry!" /C:"!FindEmptyLine!" "!HOSTSFILE!">"!HOSTSFILE!.tmp" && (
del "!HOSTSFILE!"
ren "!HOSTSFILE!.tmp" "hosts"
echo The host !HOST! unblocked
)
goto LOOP
:Common
set "HOST="
set /P "HOST=!PROMPT_TEXT! !ACTION_TEXT[%Item%]! (e.g. example.com): "
if not defined HOST (
(goto)2>nul
goto LOOP
)
set "FindEntry=^^ *!HOME! *!HOST! *$"
set "FindEntry=!FindEntry:.=\.!"
exit /b
I am so confused and all help is appreciated...
so I'm making a little game and this is the code:
#echo off
:menu
echo Welcome To My Game!!!
echo Play & echo.Exit
:: add more menu items above
set /p menInp=
if /i %menInp%==play goto initFile
if /i %menInp$==exit EXIT
:initFile
cls
if NOT exist \MyAdventureGame\AdventureGameSave.txt\ GOTO newGame
if exist \MyAdventureGame\AdventureGameSave.txt\ GOTO Load
:newGame
echo. 2>AdventureGameSave.txt
set Gold = 50
set Xp = 0
set Level = 1
GOTO Save
:Save
echo %Gold% >>AdventureGameSave.txt
echo %Xp% >>AdventureGameSave.txt
echo %Level% >>AdventureGameSave.txt
GOTO Town
:Load
:: add Load functionallity here
GOTO Town
:Town
echo Welcome to town!!!
PAUSE
Now for my question...
when i run it, no matter what I type for my menInp, my program goes to the next line, :initFile, and its almost like my if statements
(if /i %menInp%==play goto initFile
if /i %menInp$==exit EXIT)
are being ignored.
if /i %menInp$==exit EXIT
should be
if /i %menInp%==exit EXIT
or preferably
if /i "%menInp%"=="exit" EXIT
since you are unable to predict the user's input and it may contains spaces. The "s make the string a single token.
This is somewhat of a comment, but may forestall further problems.
Tip for game-generation:
If you reserve a character as a prefix for variables-you-want-to-save (eg all variables I want to save/reload start with #) then all you need to save a game is
set #>"mygamefile.txt"
and all you need to reload a game is
for /f "usebackqdelims=" %%a in ("mygamefile.txt") do set "%%a"
To zap all # variables (useful before reloading a game) use
for /f "delims==" %%a in ('set # 2^>nul') do set "%%a="
Oh and you may consider removing the terminal \ from the filename in the if exist statements (also enclose the entire filename in quotes to allow the name to contain spaces (and follow good practice regardless of whether you actually need to cater for spaces in filenames))
I'm trying to make a reminder system within batch in which there are different lines of reminders. My batch program will write to different lines in a .txt file, but it isn't working. Could you please help and try to find the issues?
#echo off
echo Enter slot # for reminder
set /p n=
cls
echo Please type in the assignment name
set /p a=
echo ----------------------------------
echo Please type in the class
set /p c=
echo ----------------------------------
echo Please type in the date due
set /p d=
cls
if %n%==1 goto l1
if %n%==2 goto l2
if %n%==3 goto l3
if %n%==4 goto l4
if %n%==5 goto l5
if %n%==6 goto l6
:l1
echo Reminder for %c% Homework! %a%,%d% > Reminder.txt
:end
:l2
echo Reminder for %c% Homework! %a%,%d% >> Reminder.txt
:end
:l3
echo Reminder for %c% Homework! %a%,%d% >>> Reminder.txt
:end
:l4
echo Reminder for %c% Homework! %a%,%d% >>>> Reminder.txt
:end
:l5
echo Reminder for %c% Homework! %a%,%d% >>>>> Reminder.txt
:end
:l6
echo Reminder for %c% Homework! %a%,%d% >>>>>> Reminder.txt
:end
Hints to fix what you've got:
The > character won't let you write to specific lines, and there's no native support in Windows batch to do such a thing.
There are two operators that use the > character: >, which redirects output to a file (replacing any existing content), and >>, which appends (adds to the end of) a file.
You've got multiple instances of :end, but that's invalid. :end is a label, which is a unique reference to that point in the code. When you add more than one, some get ignored and you get undefined behaviors, which is bad.
It looks like you're trying to use :end to exit. Use goto :EOF for that. It jumps to the built-in label :EOF, short for End Of File.
You need to handle the case where n is none of the predefined values. Currently if someone entered 7 for n, your program would get to the logic after :l1 and run it, which is wrong. Put a goto :EOF there just in case.
How to approach solving this type of issue with batch:
The only way I can think of off the top of my head to modify a specific line is to iterate through all lines using a for /f loop, rewriting each line (to a temporary file) until you encounter the one you want to change, then write your new content instead of the existing content. Then when you're done iterating, you can replace the original file with that temporary file.
You would have to do this each time you wanted to change a new line. Batch is a really simple language that doesn't have useful constructs like arrays, or the many external tools that a shell scripting language like Bash would have. It's also got some really unsophisticated runtime evaluation.
Here's a partial solution that you can combine with a few lines from your code above to achieve what you want. It prompts you for a line number, then puts the content of the newContent variable (replace with your implementation) into the file at the specified line:
REM suppresses the echo of the commands in the program
#ECHO OFF
REM sets a feature that overcomes some of the weak runtime evaluation limitations that batch has
setlocal ENABLEDELAYEDEXPANSION
REM The name of your file
set fname=file.txt
REM If our file doesn't already exist, make a new one with 6 empty lines since that's all we want for now.
if EXIST "%fname%" goto alreadyExists
for /l %%b in (1,1,6) do echo.>>"%fname%"
:alreadyExists
REM The name of a temp file
set tfile=f2.txt
REM A counter to track the line number
set counter=0
REM Input to get the line number you wish to replace
set /p replacementLine=Type the line number that should be replaced:
REM The content that goes on the replaced line
set newContent=New entry
REM Read the file, iterate through all lines.
for /f "tokens=*" %%a in (file.txt) do (
REM Add one to the counter
set /a counter=!counter!+1
REM Use the redirect '>' operator for the first line
if "!counter!"=="1" (
if "!counter!"=="%replacementLine%" (
REM We're on the line we wish to replace, so use the replacement line content
echo.%newContent% >f2.txt
) else (
REM We're NOT on the line we wish to replace, so use the original line content
echo.%%a >f2.txt
)
) else (
REM else for lines other than the first, use the append redirect '>>'
if "!counter!"=="%replacementLine%" (
REM We're on the line we wish to replace, so use the replacement line content
echo.%newContent% >>f2.txt
) else (
REM We're NOT on the line we wish to replace, so use the original line content
echo.%%a >>f2.txt
)
)
)
REM Delete the original file
del "%fname%"
REM Replace it with the modified copy
ren "%tfile%" "%fname%"
You can replace a few lines at the top get the functionality you want.
you can't write to a specific line in a file with batch. Instead you have to rewrite the complete file.
Steps: a) read the file. b) change the desired line. c) (over)write the file with the new data.
#echo off
setlocal enabledelayedexpansion
if not exist reminder.txt call :InitFile :: create the file, if it doesn't exist
set /p "n=Enter slot # for reminder: "
set /p "a=type in the assignment name: "
set /p "c=type in the class: "
set /p "d=type in the date due: "
cls
call :ReadFile
set "_Line[%n%]=Reminder for %c% Homework: %a%,%d%"
call :WriteFile
type Reminder.txt
goto :eof
:ReadFile
set x=0
for /f "delims=" %%i in (reminder.txt) do (
set /a x+=1
set "_Line[!x!]=%%i"
)
goto :eof
:WriteFile
set x=0
(for /f "tokens=2 delims==" %%i in ('set _Line[') do echo %%i)>Reminder.txt
goto :eof
:InitFile
(for /l %%i in (1,1,6) do echo Reminder %%i [empty])>Reminder.txt
goto :eof
(Note: this would make trouble with more than 9 lines because of the alphabetical sorting with set _line[, but as you need only 6 lines, this should not be a problem for you)
Note: your input shouldn't contain !
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.