Пакетные файлы - количество аргументов командной строки

99

Просто конвертирую некоторые сценарии оболочки в командные файлы, и есть одна вещь, которую я, кажется, не могу найти ... и это простой подсчет количества аргументов командной строки.

например. если у вас есть:

myapp foo bar

В ракушке:

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

В партии

  • ?? -> 2 <---- какая команда ?!
  • % * -> foo bar
  • % 0 -> myapp
  • % 1 -> foo
  • % 2 -> бар

Итак, я огляделся и либо смотрю не в том месте, либо я слепой, но, похоже, я не могу найти способ подсчитать количество переданных аргументов командной строки.

Есть ли команда, аналогичная "$ #" оболочки для командных файлов?

пс. самое близкое, что я нашел, - это перебрать% 1s и использовать shift, но мне нужно ссылаться на% 1,% 2 и т. д. позже в сценарии, так что это бесполезно.

Pyko
источник
ваша строка 2 myapp foo bar?
PsychoData 05
2
Совет: не конвертируйте sh в BAT. вместо этого скачайте cygwin и используйте его. т.е. ваши сценарии sh будут работать на машине Windows. и вам не придется переводить все sh на BAT!
Марти МакГоуэн,

Ответы:

104

Немного погуглив, вы получите следующий результат из викиучебников :

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

echo %argC%

Похоже, cmd.exe немного эволюционировал из старых дней DOS :)

нимродм
источник
7
Обратите внимание, что этот вариант forработает только для аргументов, которые выглядят как имена файлов, а не для строк параметров, таких как -?. Использование кавычек ( for %%i in ("%*") ...) работает для таких аргументов, -?но снова не работает для цитируемых аргументов из-за вложенных кавычек. Единственный надежный способ, кажется, вовлечь shift...
Фердинанд Бейер
1
MyBatchFile "*.*" "Abc"сообщает, что argC == 9. - Проголосовали против.
BrainSlugs83
Протестировали это с 2500 аргументами как функцию и использовали ее для определения массивов одинакового размера. Не спрашивайте меня, почему именно. В основном просто изучаю, на что способна партия.
T3RR0R,
44

Вы склонны обрабатывать ряд аргументов с помощью такой логики:

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

и т.п.

Если у вас более 9 аргументов, то такой подход вас не устраивает. Существуют различные приемы для создания счетчиков, которые вы можете найти здесь , но имейте в виду, что они не для слабонервных.

Дин Пови
источник
6
Вы все еще можете использовать shiftдля подсчета более 9 ... и без 10 строк одинаково выглядящего кода.
Joey
19

Функция, указанная :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

Он в основном выполняет итерацию один раз по списку (который является локальным для функции, поэтому сдвиги не повлияют на список в основной программе), считая их до тех пор, пока он не закончится.

Он также использует изящный трюк, передавая имя возвращаемой переменной, которая будет установлена ​​функцией.

Основная программа просто показывает, как его вызвать, а затем повторяет аргументы, чтобы убедиться, что они не затронуты:

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"
Paxdiablo
источник
как вы можете изменить поток на основе этого? (может быть другой вопрос, но я думаю, что здесь тоже будет очень удобен короткий пример!)
n611x007
@ n611x007, куда echo Count is %argc%вы должны вставить свой собственный код, который может проверить правильность этого подсчета, а затем двигаться дальше.
Kenny
7

Попробуй это:

SET /A ARGS_COUNT=0    
FOR %%A in (%*) DO SET /A ARGS_COUNT+=1    
ECHO %ARGS_COUNT%
Дэвид
источник
6
этот ответ чем-то отличается от ответа @nimrod ? ...
голубоватый
1
проверьте комментарий @FerdinandBeyer в nimrodm's. не собираюсь голосовать против, потому что у вас 21 представитель 8)
n611x007
6

Если количество аргументов должно быть точным числом (меньше или равно 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
Slu
источник
2

Избегает использования одного 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
)

Вывод:

count_cmdline_args.bat testing more_testing arg3 another_arg

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

РЕДАКТИРОВАТЬ: Используемый здесь «трюк» включает:

  1. Создание строки, представляющей текущую вычисляемую переменную аргумента командной строки (например, «% 1», «% 2» и т. Д.) С использованием строки, содержащей символ процента ( %%) и переменную счетчика arg_idxна каждой итерации цикла.

  2. Сохранение этой строки в переменной curr_arg_label.

  3. Передача и этой строки ( !curr_arg_label!), и имени возвращаемой переменной ( curr_arg_value) в примитивную подпрограмму get_value.

  4. В подпрограмме значение первого аргумента ( %1) используется слева от assignment ( set), а значение второго аргумента ( %2) - справа. Однако, когда передается второй аргумент подпрограммы, он преобразуется в значение аргумента командной строки основной программы интерпретатором команд. То есть передается не, например, «% 4», а любое значение, которое содержит четвертая переменная аргумента командной строки («another_arg» в примере использования).

  5. Затем переменная, переданная подпрограмме как возвращаемая переменная ( curr_arg_value), проверяется на неопределенность, что может произойти, если в настоящее время вычисленный аргумент командной строки отсутствует. Первоначально это было сравнение значения возвращаемой переменной, заключенного в квадратные скобки, с пустыми квадратными скобками (это единственный известный мне способ тестирования аргументов программы или подпрограммы, которые могут содержать кавычки и были пропущены после фазы проб и ошибок), но с тех пор было исправлено, как сейчас.

костер
источник
1
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, как и / или почему он решает проблему, улучшит долгосрочную ценность ответа.
thewaywere
@thewaywewere спасибо, я все время забываю, что для многих людей (скорее всего, большинства) лучше иметь текстовое описание, а не "понятный" код.
fen-fire
@ fen-fire, за исключением того, что код не требует пояснений.
Loduwijk
1

Последний ответ был два года назад, но мне нужна была версия для более чем девяти аргументов командной строки. Может быть, еще один ...

@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

Решением являются первые 19 строк, которые преобразуют все аргументы в переменные в стиле c. Все остальное просто проверяет результат и показывает преобразование в локальные аргументы. Вы можете ссылаться на аргументы по индексу в любой функции.

Казимир
источник
0

Надежным решением является делегирование подсчета подпрограмме, вызываемой с помощью call; подпрограмма использует gotoоператоры для имитации цикла, в котором shiftиспользуется для итеративного использования аргументов (только для подпрограммы):

@echo off
setlocal

:: Call the argument-counting subroutine with all arguments received,
:: without interfering with the ability to reference the arguments
:: with %1, ... later.
call :count_args %*

:: Print the result.
echo %ReturnValue% argument(s) received.

:: Exit the batch file.
exit /b

:: Subroutine that counts the arguments given.
:: Returns the count in %ReturnValue%
:count_args
  set /a ReturnValue = 0
  :count_args_for

    if %1.==. goto :eof

    set /a ReturnValue += 1

    shift
  goto count_args_for
mklement0
источник