Batch files - number of command line arguments

2019-01-13 00:51发布

问题:

Just converting some shell scripts into batch files and there is one thing I can't seem to find...and that is a simple count of the number of command line arguments.

eg. if you have:

myapp foo bar

In Shell:

  • $# -> 2
  • $* -> foo bar
  • $0 -> myapp
  • $1 -> foo
  • $2 -> bar

In batch

  • ?? -> 2 <---- what command?!
  • %* -> foo bar
  • %0 -> myapp
  • %1 -> foo
  • %2 -> bar

So I've looked around, and either I'm looking in the wrong spot or I'm blind, but I can't seem to find a way to get a count of number of command line arguments passed in.

Is there a command similar to shell's "$#" for batch files?

ps. the closest i've found is to iterate through the %1s and use 'shift', but I need to refernece %1,%2 etc later in the script so that's no good.

回答1:

Googling a bit gives you the following result from wikibooks:

set argC=0
for %%x in (%*) do Set /A argC+=1

echo %argC%

Seems like cmd.exe has evolved a bit from the old DOS days :)



回答2:

You tend to handle number of arguments with this sort of logic:

IF "%1"=="" GOTO HAVE_0
IF "%2"=="" GOTO HAVE_1
IF "%3"=="" GOTO HAVE_2

etc.

If you have more than 9 arguments then you are screwed with this approach though. There are various hacks for creating counters which you can find here, but be warned these are not for the faint hearted.



回答3:

The function :getargc below may be what you're looking for.

@echo off
setlocal enableextensions enabledelayedexpansion
call :getargc argc %*
echo Count is %argc%
echo Args are %*
endlocal
goto :eof

:getargc
    set getargc_v0=%1
    set /a "%getargc_v0% = 0"
:getargc_l0
    if not x%2x==xx (
        shift
        set /a "%getargc_v0% = %getargc_v0% + 1"
        goto :getargc_l0
    )
    set getargc_v0=
    goto :eof

It basically iterates once over the list (which is local to the function so the shifts won't affect the list back in the main program), counting them until it runs out.

It also uses a nifty trick, passing the name of the return variable to be set by the function.

The main program just illustrates how to call it and echos the arguments afterwards to ensure that they're untouched:

C:\Here> xx.cmd 1 2 3 4 5
    Count is 5
    Args are 1 2 3 4 5
C:\Here> xx.cmd 1 2 3 4 5 6 7 8 9 10 11
    Count is 11
    Args are 1 2 3 4 5 6 7 8 9 10 11
C:\Here> xx.cmd 1
    Count is 1
    Args are 1
C:\Here> xx.cmd
    Count is 0
    Args are
C:\Here> xx.cmd 1 2 "3 4 5"
    Count is 3
    Args are 1 2 "3 4 5"


回答4:

Try this:

SET /A ARGS_COUNT=0    
FOR %%A in (%*) DO SET /A ARGS_COUNT+=1    
ECHO %ARGS_COUNT%


回答5:

If the number of arguments should be an exact number (less or equal to 9), then this is a simple way to check it:

if "%2" == "" goto args_count_wrong
if "%3" == "" goto args_count_ok

:args_count_wrong
echo I need exactly two command line arguments
exit /b 1

:args_count_ok


回答6:

Avoids using either shift or a for cycle at the cost of size and readability.

@echo off
setlocal EnableExtensions EnableDelayedExpansion
set /a arg_idx=1
set "curr_arg_value="
:loop1
if !arg_idx! GTR 9 goto :done
set curr_arg_label=%%!arg_idx!
call :get_value curr_arg_value !curr_arg_label!
if defined curr_arg_value (
  echo/!curr_arg_label!: !curr_arg_value!
  set /a arg_idx+=1
  goto :loop1
)
:done
set /a cnt=!arg_idx!-1
echo/argument count: !cnt!
endlocal
goto :eof

:get_value
(
  set %1=%2
)

Output:

count_cmdline_args.bat testing more_testing arg3 another_arg

%1: testing
%2: more_testing
%3: arg3
%4: another_arg
argument count: 4

EDIT: The "trick" used here involves:

  1. Constructing a string that represents a currently evaluated command-line argument variable (i.e. "%1", "%2" etc.) using a string that contains a percent character (%%) and a counter variable arg_idx on each loop iteration.

  2. Storing that string into a variable curr_arg_label.

  3. Passing both that string (!curr_arg_label!) and a return variable's name (curr_arg_value) to a primitive subprogram get_value.

  4. In the subprogram its first argument's (%1) value is used on the left side of assignment (set) and its second argument's (%2) value on the right. However, when the second subprogram's argument is passed it is resolved into value of the main program's command-line argument by the command interpreter. That is, what is passed is not, for example, "%4" but whatever value the fourth command-line argument variable holds ("another_arg" in the sample usage).

  5. Then the variable given to the subprogram as return variable (curr_arg_value) is tested for being undefined, which would happen if currently evaluated command-line argument is absent. Initially this was a comparison of the return variable's value wrapped in square brackets to empty square brackets (which is the only way I know of testing program or subprogram arguments which may contain quotes and was an overlooked leftover from trial-and-error phase) but was since fixed to how it is now.



回答7:

The last answer was two years ago now, but I needed a version for more than nine command line arguments. May be another one also does...

@echo off
setlocal

set argc_=1
set arg0_=%0
set argv_=

:_LOOP
set arg_=%1
if defined arg_ (
  set arg%argc_%_=%1
  set argv_=%argv_% %1
  set /a argc_+=1
  shift
  goto _LOOP
)
::dont count arg0
set /a argc_-=1
echo %argc_% arg(s)

for /L %%i in (0,1,%argc_%) do (
  call :_SHOW_ARG arg%%i_ %%arg%%i_%%
)

echo converted to local args
call :_LIST_ARGS %argv_%
exit /b


:_LIST_ARGS
setlocal
set argc_=0
echo arg0=%0

:_LOOP_LIST_ARGS
set arg_=%1
if not defined arg_ exit /b
set /a argc_+=1
call :_SHOW_ARG arg%argc_% %1
shift
goto _LOOP_LIST_ARGS


:_SHOW_ARG
echo %1=%2
exit /b

The solution is the first 19 lines and converts all arguments to variables in a c-like style. All other stuff just probes the result and shows conversion to local args. You can reference arguments by index in any function.