Пакетні файли - кількість аргументів командного рядка


99

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

напр. якщо у вас є:

myapp foo bar

У оболонці:

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

У партії

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

Отже, я озирнувся, або я шукаю не те місце, або я сліпий, але, здається, не можу знайти спосіб отримати кількість аргументів командного рядка, переданих.

Чи існує команда, схожа на оболонку "$ #" для пакетних файлів?

пс. Найближче, що я знайшов, - це переглядати% 1s і використовувати "shift", але мені потрібно звернутися до% 1,% 2 і т.д. пізніше в сценарії, так що це погано.


ваш рядок 2 myapp foo bar?
PsychoData

2
порада: не перетворюйте sh в BAT. замість цього завантажте cygwin і використовуйте його замість цього. тобто ваші скрипти sh будуть працювати на машині Windows. і вам не доведеться перекладати кожен ш на BAT!
Марті Макгоуан,

Відповіді:


104

Трохи погугливши, ви отримуєте такий результат з вікікниг :

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

echo %argC%

Здається, cmd.exe трохи перетворився на старі часи DOS :)


7
Зауважте, що цей варіант forпрацює лише для аргументів, які виглядають як імена файлів, а не для рядків опцій, таких як -?. Використання quotes ( 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 рядків однаково виглядаючого коду.
Джої

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"

як ви можете змінити потік на основі цього? (можливо, інше питання, але я думаю, що короткий приклад тут також буде дуже зручним!)
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

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) використовується в лівій частині призначення ( set), а значення другого аргументу ( %2) - в правій. Однак, коли передається аргумент другої підпрограми, він перетворюється у значення аргументу командного рядка основної програми інтерпретатором команд. Тобто те, що передається, не є, наприклад, "% 4", а яке б значення не містила четверта змінна аргументу командного рядка ("another_arg" у зразку використання).

  5. Тоді змінна, надана підпрограмі як зворотна змінна (curr_arg_value ), перевіряється на невизначеність, що могло б статися, якщо в даний момент обчислюваний аргумент командного рядка відсутній. Спочатку це було порівняння значення змінної повернення, загорнутого в квадратні дужки, з порожніми квадратними дужками (це єдиний спосіб, яким я знаю тестування аргументів програми або підпрограми, які можуть містити лапки, і це було залишено без уваги залишки з фази спроб і помилок), але з тих пір було зафіксовано, як зараз.


1
Хоча цей код може відповісти на запитання, надання додаткового контексту щодо того, як та / або чому він вирішує проблему, покращило б довгострокову цінність відповіді.
thewaywewere

@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 рядків, які перетворюють усі аргументи на змінні у стилі, подібному до с. Всі інші речі просто перевіряють результат і показують перетворення в локальні аргументи. Ви можете посилатися на аргументи за індексом у будь-якій функції.


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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.