3

Given an optional port argument, where the port number can vary in length, how do I obtain the port number from the batch script's command line arguments?

Example:

foo.bat --foo bar --port 80 --bar foo

Should output:

80

I got this far, trying to use substring.

set CMD_LINE_ARGS=%*
@rem Remove all chars and port arg
set PORT_ARG_REMOVED=%CMD_LINE_ARGS:*-port =%
@rem Obviously, this is where I fail to remove trailing chars
set PORT_NUM=%PORT_ARG_REMOVED: *=%
echo %PORT_NUM%

Edit

The answer I chose is because it fits with my very particular use case, where all arguments were being passed through to the command that I was wrapping. And, I only needed the value for a particular optional argument. No looping required.

There are some very nice answers here for dealing with optional argument parsing in general. So, feel free to upvote everyone.

4 Answers 4

3

Seeing as you 'would have really have liked to see the substring work', here's your script structured and coded as you'd intended.

@Set "CMD_LINE_ARGS=%*"
@Rem Remove all chars and port arg
@Set "PORT_ARG_REMOVED=%CMD_LINE_ARGS:*-port =%"
@Rem Remove trailing chars
@Set "PORT_NUM=%PORT_ARG_REMOVED: ="&:"%"
@Echo %PORT_NUM%
@Pause
Sign up to request clarification or add additional context in comments.

5 Comments

Gah! Yes. But, can you explain the syntax there in line 5? It seems so odd. Looks like you even have an uneven number of double quotes, but it works.
To be more precise, I see that you are searching for space, which I assume is why you needed to quote the entire set parameter. Then, replacing that with nothing, via =". But, the trailing &: is lost on me.
It is actually replacing the space with "&:", so the entire line becomes @Set "PORT_NUM=80"&:"" after variable expansion; the & lets concatenated another command on the same line, the remaining thing :"" is nothing but a jump label, so this is ignored...
This technique can fail if the value of an optional parameter can mimic the name of a parameter. For example, foo.bat --foo --port --port 80 --bar foo would yield port=--port
The OP has already intimated that they are sure that the input parameters will match the format required by the script, and that they are happy that it meets their requirements.
2

You can use %1, %2 etc for the separate command line arguments. In your case --port would be %3 and its value would be %4.

Fortunately, there is also the shift command, which shifts all arguments, to 2 becomes 1, 3 becomes 2, etc.

That means that you can 'scrape' all command line parameters in a loop. You keep shifting and when you encounter --port, you know that the next one is going to be the port number, which you can store in an appropriate variable (using set) for later use.

If you implement it like that, you can implement a bunch of optional parameters and the order won't matter either.

So in code, your 'foo.bat' could look like this:

@echo off
:: No parameters given? Then run ourselves with the defaults from the question.
if "%1"=="" goto none

:: Actual reading of parameters starts here.

:loop
if "%1"=="--port" set port=%2
:: Of course you need the other ifs only if you're interested in these parameters
if "%1"=="--foo" set foo=%2
if "%1"=="--bar" set bar=%2

:: Shift all parameters, except %0. Do it twice to skip the parameter and its value.
shift /1
shift /1

:: As long as there are more, keep looping. You can even parse more than 10 parameters this way!
if not "%1"=="" goto loop

:: Output what we've found
echo The specified port is %port%
echo Foo is set to %foo%
echo The bar is at %bar%
pause

exit /b

:: If no parameters are given, call ourselves with example parameters.
:none
call %0 --foo bar --port 80 --bar foo

Dressed down version without the demo crap that only displays the port number. I think this is a drop-in replacement for your current code.

@echo off
:loop
if "%1"=="--port" set port=%2
shift /1
shift /1
if not "%1"=="" goto loop
echo %port%

7 Comments

I agree that this works. But, it seems to be doing so much work all to avoid something that I thought worked in batch scripting. I'll definitely consider this as an answer.
You could use a slightly different approach of splitting the string and iterating it with a loop, but the effect is mostly the same. There are also ways to get substrings, but they work with a count of characters. There is a string replacement too, but I don't think that really helps here. And it's not that much work, if you strip all the overhead for demo purposes, there are just 5 lines to get the port number.
9 lines and requires a loop (could argue 7 if "if" statement is 1 line) vs. only 3 above without a loop. You could argue 4 in my approach, but I have to preserve the command line arguments in both, anyways. Just saying...
I've included the short version at the bottom of the answer. 5 for the loop, 7 if you include echo off and echoing the port. But like I said, it's not as brief as the version in your question. The difference though is that this one works. I was under the impression that you were looking for a working solution, not necessarily a byte squeezing codegolf hack. That I cannot give you. I suck at short code.
Lol. I'm not trying to argue over LOC. But..I didn't need to check for no arguments before and now I do. And, I need a label to jump to, if I do. So, the 7 wasn't with the output.
|
2

A simpler and obvious approach that get all arguments no matters how many:

@echo off
setlocal EnableDelayedExpansion

set "arg="
for %%a in (%*) do (
   if not defined arg (
      set "arg=%%a"
   ) else (
      set "!arg:~2!=%%a"
      set "arg="
   )
)

echo foo = %foo%
echo port = %port%
echo bar = %bar%

If you like short code, the version below do the same task in less lines:

@echo off

set "args= %*"
set "args=%args: --=+%"
set "args=%args: ==%"
set args=%args:+=&set %

echo foo = %foo%
echo port = %port%
echo bar = %bar%

If you want to review how the shorter version works, just remove the @echo off line and execute it.

1 Comment

I considered that, but it means that somebody can overwrite existing environment variables by just passing them as command line arguments, influencing your script if you use those. That may be considered an advantage, or a vulnerability. I think it's the latter.
0

I would first remove the --port part using sub-string replacement, then get the value by a for /F loop, like this:

set CMD_LINE_ARGS=%*
@rem Remove everything up to `--port` +  a space:
set "PORT_ARG_REMOVED=%CMD_LINE_ARGS:*--port =%"
@rem Extract first space- (or tab-)separated item:
for /F "tokens=1" %%A in ("%PORT_ARG_REMOVED%") do set "PORT_NUM=%%A"
echo/%PORT_NUM%

The for /F approach has got the great advantage that it even works when multiple consecutive SPACEs are provided to separate the command line arguments.


If you want the code to allow all standard token separators (which are SPACE, TAB, ,, ;, = and the character with code 0xFF) rather than only the SPACE, you can change the code to this:

set "FLAG="
@rem Use a standard `for` loop to walk through the arguments:
for %%A in (%*) do (
    @rem Capture the argument following the `--port` item:
    if defined FLAG if not defined PORT_NUM set "PORT_NUM=%%~A"
    @rem Use a `FLAG` variable to delay capturing argument by an iteration;
    @rem to match `--port` case-sensitively, remove the `/I` option:
    if /I "%%~A"=="--port" set "FLAG=#"
)
echo/%PORT_NUM%

Actually I prefer that way, because there is no string manipulation involved, because the list of arguments is parsed in the nature of cmd.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.