批处理文件 - 命令行参数的数量

时间:2020-12-04 21:23:43

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.

只是将一些shell脚本转换为批处理文件,有一件事我似乎找不到......这是一个简单的命令行参数数量。

eg. if you have:

例如。如果你有:

myapp foo bar

In Shell:

壳牌:

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

In batch

批量生产

  • ?? -> 2 <---- what command?!
  • ?? - > 2 <----什么命令?!
  • %* -> foo bar
  • %* - > foo吧
  • %0 -> myapp
  • %0 - > myapp
  • %1 -> foo
  • %1 - > foo
  • %2 -> bar
  • %2 - >吧

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?

是否有类似于shell的“$#”命令用于批处理文件?

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.

PS。我发现最接近的是遍历%1s并使用'shift',但我需要在脚本中稍后引用%1,%2等,这样就没有用了。

7 个解决方案

#1


84  

Googling a bit gives you the following result from wikibooks:

谷歌搜索从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 :)

好像cmd.exe已经从旧的DOS时代发展了一下:)

#2


33  

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.

如果你有超过9个参数,那么你会对这种方法感到失望。你可以在这里找到各种用于创建计数器的黑客,但要警告这些不是为了胆小的人。

#3


18  

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

功能:下面的getargc可能就是你要找的东西。

@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


6  

Try this:

尝试这个:

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

#5


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:

如果参数的数量应该是一个确切的数字(小于或等于9),那么这是一种检查它的简单方法:

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


2  

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

避免使用shift或for循环,但需要考虑尺寸和可读性。

@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.

    使用包含百分比字符(%%)和每个循环迭代的计数器变量arg_idx的字符串构造表示当前求值的命令行参数变量(即“%1”,“%2”等)的字符串。

  2. Storing that string into a variable curr_arg_label.

    将该字符串存储到变量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.

    将该字符串(!curr_arg_label!)和返回变量的名称(curr_arg_value)传递给原始子程序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).

    在子程序中,它的第一个参数(%1)值在赋值(set)的左侧使用,而在右侧使用它的第二个参数(%2)值。但是,当第二个子程序的参数被传递时,它将被命令解释器解析为主程序的命令行参数的值。也就是说,传递的内容不是,例如,“%4”,而是第四个命令行参数变量保持的值(样本用法中的“another_arg”)。

  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.

    然后测试给作为返回变量(curr_arg_value)的子程序的变量是否未定义,如果当前评估的命令行参数不存在则会发生这种变量。最初这是将方括号中包含的返回变量的值与空方括号进行比较(这是我知道测试程序或子程序参数的唯一方法,它可能包含引号并且是试错法阶段的一个被忽略的剩余部分)但是从那以后就固定了。

#7


1  

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.

解决方案是前19行,并将所有参数转换为类似c的样式的变量。所有其他东西只是探测结果并显示转换为本地args。您可以在任何函数中通过索引引用参数。

#1


84  

Googling a bit gives you the following result from wikibooks:

谷歌搜索从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 :)

好像cmd.exe已经从旧的DOS时代发展了一下:)

#2


33  

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.

如果你有超过9个参数,那么你会对这种方法感到失望。你可以在这里找到各种用于创建计数器的黑客,但要警告这些不是为了胆小的人。

#3


18  

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

功能:下面的getargc可能就是你要找的东西。

@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


6  

Try this:

尝试这个:

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

#5


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:

如果参数的数量应该是一个确切的数字(小于或等于9),那么这是一种检查它的简单方法:

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


2  

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

避免使用shift或for循环,但需要考虑尺寸和可读性。

@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.

    使用包含百分比字符(%%)和每个循环迭代的计数器变量arg_idx的字符串构造表示当前求值的命令行参数变量(即“%1”,“%2”等)的字符串。

  2. Storing that string into a variable curr_arg_label.

    将该字符串存储到变量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.

    将该字符串(!curr_arg_label!)和返回变量的名称(curr_arg_value)传递给原始子程序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).

    在子程序中,它的第一个参数(%1)值在赋值(set)的左侧使用,而在右侧使用它的第二个参数(%2)值。但是,当第二个子程序的参数被传递时,它将被命令解释器解析为主程序的命令行参数的值。也就是说,传递的内容不是,例如,“%4”,而是第四个命令行参数变量保持的值(样本用法中的“another_arg”)。

  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.

    然后测试给作为返回变量(curr_arg_value)的子程序的变量是否未定义,如果当前评估的命令行参数不存在则会发生这种变量。最初这是将方括号中包含的返回变量的值与空方括号进行比较(这是我知道测试程序或子程序参数的唯一方法,它可能包含引号并且是试错法阶段的一个被忽略的剩余部分)但是从那以后就固定了。

#7


1  

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.

解决方案是前19行,并将所有参数转换为类似c的样式的变量。所有其他东西只是探测结果并显示转换为本地args。您可以在任何函数中通过索引引用参数。