How can I pass arguments to a batch file?

2,152,122

Solution 1

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.

Solution 2

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.

Solution 3

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

Solution 4

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.

Solution 5

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.

Share:
2,152,122
Keng
Author by

Keng

Personal Achievables: Medical Note: I now have irreversible damage to my kidneys and liver after having used this site.

Updated on July 24, 2022

Comments

  • Keng
    Keng almost 2 years

    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
    
  • Denilson Sá Maia
    Denilson Sá Maia over 14 years
    Please note that %* does not work everywhere! For instance, it does not work with DOSBox 0.73 (maybe this is a bug that should be reported).
  • Kef Schecter
    Kef Schecter over 12 years
    It's not a bug because %* never worked in MS-DOS or Win9x in the first place.
  • Royi Namir
    Royi Namir over 11 years
    %% is only for if and for ?
  • Steve Midgley
    Steve Midgley over 11 years
    It's worse than that - %% is used to prefix variables and command line parameters inside batch files. But when you use these commands from the command line, you use only % to prefix. Example: inside batch: for %%d in (*) do echo %%d from command line: for %d in (*) do echo %d
  • ruffin
    ruffin over 9 years
    If you're as dumb as me, your mind was looking for echo %1 %2 and was thrown off by the non cut-and-pasteable simplest case with a @ and a fake-command with params, thinking we'd get fake-command.bat's contents later (in which case, the overcomplicated fake-command.bat might have echo %2 %4 to ignore the param names). Wrong, doofus. TL;DR: Don't be as dumb as me. 1. echo echo %1 %2 > test.bat 2. test word1 word2. 3. Profit.
  • NathanAldenSr
    NathanAldenSr about 8 years
    I think shift "pops" the argument from the list. The assumption by OP was that %* would only output the remaining arguments, but it doesn't work that way, as @Joey said.
  • Paint_Ninja
    Paint_Ninja almost 8 years
    You can close the cmd window after the process has closed using the following command in a batch script: start /wait java %~n1
  • Jono_2007
    Jono_2007 almost 8 years
    Been looking at using this method as I would like to pass arguments into a batch file in this manner. However I notice that after the variables are set, even after exiting the batch file the parameters are still set in the cmd if accessed and the batch has ended, they are not restored to their previous state. Should this method handle that situation?
  • icc97
    icc97 over 7 years
    @SteveMidgley I upvoted your comment probably a year or so ago. I then promptly forgot about it and just today I was trying and staring confusingly at my for loop in the command line and wondering why it farted and did nothing else. So here's another virtual upvote from me. I'll be back in another year or so when I hit the same issue again.
  • Chris
    Chris over 7 years
    What's the significance of dot/period in these equality operations?
  • Carl
    Carl over 7 years
    Basically if %1 is empty, this will end up being IF . == . and so the GOTO will happen. We use x here so: IF x%1 == x -> IF x==x -> true
  • Kevin Edwards
    Kevin Edwards about 7 years
    for /F "tokens=*" %%a in ('echo %*') do set "all_args=%%a"
  • jeb
    jeb about 7 years
    @KevinEdwards Then try it with one &, your solution works only with simple content
  • jeb
    jeb about 7 years
    @KevinEdwards I tested it, test.bat ^& and it fails. Only test.bat "&" works, but that wasn't my point. You can't use %*, %1 in a safe way without the REM technic
  • Kevin Edwards
    Kevin Edwards about 7 years
    Ah, yes, a caret doesn't escape it properly. You need quotes, which is feasible for all the use cases I've come across. I just wanted to pass it along. Thanks for testing it! Cheers :)
  • Explorer09
    Explorer09 about 7 years
    I have a concern about this method (echo-on-and-rem-piped-to-a-file trick): Although batch and cmd.exe don't allow newlines in command arguments, but for shells that allow it when calling the script, the trick breaks. And don't laugh, because PowerShell allow newlines in arguments. Using low-level CreateProcess() function is also possible to make a newline and break it.
  • jeb
    jeb about 7 years
    @Explorer09 Batch and also cmd.exe allowes both newlines in arguments, but it's tricky. The echo-on-and-rem-redirect-to-a-file trick can also fetch multiline arguments, but that isn't bullet proof, see SO:...multi line text as argument...
  • Explorer09
    Explorer09 about 7 years
    @jeb The SO answer you mentioned does allow multi-line argument parsing, but doesn't protect against command injection from line 2 and onward. I fear there will be no true bullet-proof solution until there's an extension syntax (e.g. !1! !2! or !!1 !!2) that can truly delay-expand the arguments.
  • jwdonahue
    jwdonahue over 6 years
    Over complicated and mostly not directly related the OP's question.
  • jwdonahue
    jwdonahue over 6 years
    We usually reserve the digits for command line processing and the double percent only applies to the for statement as far as I know. @for %%2 in (testing) do @echo %%2 %1 in a batch file, produces testing 1 when it is called with 1 (test 1).
  • jwdonahue
    jwdonahue over 6 years
    True enough, but it barely touches the subject matter.
  • jwdonahue
    jwdonahue over 6 years
    On topic and better than some of the rest of the answers on this thread.
  • jwdonahue
    jwdonahue over 6 years
    Advising the public to put random script files in the system32 directory is irresponsible. This answer really doesn't scratch the surface of the topic of this thread.
  • jwdonahue
    jwdonahue over 6 years
    This answer is wrong. It actually results in fake-command /u admin /p password admin password foo bar. This was pointed out by @Joey many years ago.
  • Adriano G. V. Esposito
    Adriano G. V. Esposito over 6 years
    You should aks for what is the value of a "unprovided argument"? Againts what you should check an "unprovided argument"? Then, if you don't have an answer, you should use a trick like the one of the dots. Remember that, like stated here ss64.com/nt/if.html "You can in fact use almost any character for this a '~' or curly brackets, { } or even the number 4, but square brackets tend to be chosen because they don't have any special meaning."
  • Fotios Basagiannis
    Fotios Basagiannis over 5 years
    The two shifts are pointless in this case. AFTER them %1 would become foo and %2 would become bar but this is not used in any way and %* is not affected by the shift command
  • Max
    Max over 4 years
    Extended it to support flag args (without values). Sorry for lack of formatting but it doesn't work in comments :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 ) else ( if not "%__var%"=="" ( endlocal & set %__var:~1%=1 ) )
  • jeb
    jeb over 4 years
    But fails with many different arguments, like test.bat *.txt, test.bat cat^&dog or Test.bat /?
  • jeb
    jeb over 4 years
    The answer is wrong. %%1 doesn't access a command line argument, and it's also wrong for the IF command. Only the FOR command (in batch files) requires double percents, but even then %%1 would define a FOR parameter and doesn't access arguments
  • jeb
    jeb over 4 years
    @jwdonahue Yes, it's complicated (but not over complicated). But especially for passwords it could be necessary, at least some of my passwords contain ampersands & and also quotes "
  • ruffin
    ruffin almost 4 years
    Five years later, I'm back reading my comment w/ confusion. Seem to have meant "to create a .bat with two parameters, literally type echo echo %1 %2 > test.bat. The test.bat file will have echo %1 %2 in it (you could've also saved it from a text editor). Now type test word1 word2 to call & see the parameters worked. word1 word2 will be echoed to the command line. (echo %2 %4 would've ignored /u and /p so you could've called test /u word1 /p word2 to get the same result). @ before a cmd in a bat file means the cmd isn't repeated.
  • NTDLS
    NTDLS almost 3 years
    Great answer. Concise and easy to follow. 🔥
  • Alan
    Alan about 2 years
    I know, a long time ago. But thank you! Simple is best, we can figure out the rest. Huh, a poem.
  • Love and peace - Joe Codeswell
    Love and peace - Joe Codeswell about 2 years
    "Simple is best, we can figure out the rest"! Words to live by. Thanks, Alan.
  • Flo
    Flo about 2 years
    What a piece of beauty! For future readers, I only want to highlight that the -f flag is not actually included in the parse-section yet. Just a heads up!
  • kodybrown
    kodybrown about 2 years
    I accepted your edit @Flo. Thanks!
  • Sayan Dasgupta
    Sayan Dasgupta about 2 years
    what does fake-command do or what does it mean ?
  • DennisVM-D2i
    DennisVM-D2i about 2 years
    Joey is right - shift does not affect '%*'.
  • DennisVM-D2i
    DennisVM-D2i about 2 years
    You might want some quote handling/protection too, e.g.: SetLocal Set dbName=%1 Rem dbName If Not Defined dbName Goto SyntaxErr REM Remove all (/any) the quotes Set dbName=%dbName:"=% If "%dbName%" == "" Goto SyntaxErr