Pliki wsadowe - liczba argumentów wiersza poleceń


99

Po prostu przekonwertowałem niektóre skrypty powłoki na pliki wsadowe i wydaje mi się, że jest jedna rzecz, której nie mogę znaleźć ... a jest to prosta liczba argumentów wiersza poleceń.

na przykład. Jeśli masz:

myapp foo bar

W skorupkach:

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

W partii

  • ?? -> 2 <---- jakie polecenie ?!
  • % * -> foo bar
  • % 0 -> myapp
  • % 1 -> foo
  • % 2 -> bar

Rozejrzałem się więc dookoła i albo szukam w złym miejscu, albo jestem ślepy, ale nie mogę znaleźć sposobu na obliczenie liczby przekazanych argumentów wiersza poleceń.

Czy istnieje polecenie podobne do polecenia powłoki „$ #” dla plików wsadowych?

ps. najbliższe, jakie znalazłem, to iteracja przez% 1s i użycie 'shift', ale muszę ponownie sprawdzić% 1,% 2 itd. w dalszej części skryptu, więc to nie jest dobre.


twój ciąg to 2 myapp foo bar?
— PsychoData,

2
rada: nie zamieniaj sh na BAT. zamiast tego pobierz cygwin i użyj go zamiast tego. tzn. twoje skrypty sh będą działać na komputerze z systemem Windows. i nie będziesz musiał tłumaczyć każdego sh na BAT!
— Marty McGowan

Odpowiedzi:


104

Trochę googlując daje następujący wynik z wikibooków :

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

echo %argC%

Wygląda na to, że cmd.exe wyewoluował trochę ze starego DOSa :)


7
Zauważ, że ten wariant fordziała tylko dla argumentów, które wyglądają jak nazwy plików, a nie dla łańcuchów opcji, takich jak -?. Używanie cudzysłowów ( for %%i in ("%*") ...) działa w przypadku argumentów podobnych, -?ale znowu kończy się niepowodzeniem dla argumentów cytowanych w cudzysłowie z powodu zagnieżdżonych cudzysłowów. Wydaje się, że jedyną solidną metodą jest shift…
— Ferdinand Beyer

1
MyBatchFile "*.*" "Abc"zgłasza, że ​​argC == 9. - Głosowano w dół.
— BrainSlugs83

Przetestowałem to na 2500 argumentach jako funkcję i użyłem go do zdefiniowania tablic o tym samym rozmiarze. Nie pytaj mnie dokładnie, dlaczego. Głównie po prostu uczenie się, do czego jest zdolna partia.
— T3RR0R

44

Masz tendencję do obsługi wielu argumentów za pomocą tego rodzaju logiki:

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

itp.

Jeśli masz więcej niż 9 argumentów, to takie podejście nie daje Ci rady. Istnieją różne sposoby tworzenia liczników, które można znaleźć tutaj , ale ostrzegam, że nie są one dla osób o słabych nerwach.


6
Nadal możesz shiftliczyć więcej niż 9 ... i bez 10 linii równie wyglądającego kodu.
— Joey

19

Poniższa funkcja :getargcmoże być tym, czego szukasz.

@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

Zasadniczo wykonuje iterację raz po liście (która jest lokalna dla funkcji, więc przesunięcia nie wpłyną na listę z powrotem w głównym programie), zliczając je, aż się wyczerpie.

Używa również sprytnej sztuczki, przekazując nazwę zmiennej zwracanej, która ma być ustawiona przez funkcję.

Główny program po prostu ilustruje, jak go wywołać, i wyświetla później argumenty, aby upewnić się, że są nietknięte:

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"

jak możesz na tej podstawie zmienić przepływ? (może być inne pytanie, ale myślę, że krótki przykład byłby tu również bardzo wygodny!)
— n611x007

@ n611x007, gdzie echo Count is %argc%można wstawić własny kod, który mógłby sprawdzić, czy ta liczba jest poprawna, a następnie przejść dalej.
— kenny

7

Spróbuj tego:

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

6
czy ta odpowiedź różni się w jakiś sposób od @nimrod one ? ...
— niebieskawe

1
sprawdź komentarz @FerdinandBeyer w nimrodm's. nie zamierzam głosować przeciw, ponieważ masz 21 rep 8)
— n611x007

6

Jeśli liczba argumentów powinna być dokładną liczbą (mniejszą lub równą 9), to w prosty sposób można to sprawdzić:

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

Unika używania jednego shiftlub forcyklu kosztem rozmiaru i czytelności.

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

Wynik:

count_cmdline_args.bat testing more_testing arg3 another_arg

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

EDYCJA: Zastosowana tutaj „sztuczka” obejmuje:

  1. Skonstruowanie łańcucha, który reprezentuje aktualnie ocenianą zmienną argumentu wiersza polecenia (np. „% 1”, „% 2” itp.) Przy użyciu łańcucha zawierającego znak procentu ( %%) i zmienną licznika arg_idxw każdej iteracji pętli.

  2. Przechowywanie tego ciągu w zmiennej curr_arg_label.

  3. Przekazanie zarówno tego ciągu ( !curr_arg_label!), jak i nazwy zmiennej zwracanej ( curr_arg_value) do podprogramu pierwotnego get_value.

  4. W podprogramie wartość pierwszego argumentu ( %1) jest używana po lewej stronie przypisania ( set), a wartość drugiego argumentu ( %2) po prawej stronie. Jednak przekazanie argumentu drugiego podprogramu powoduje, że interpreter poleceń przekształca go na wartość argumentu wiersza poleceń programu głównego. Oznacza to, że przekazywane jest nie, na przykład, „% 4”, ale jakąkolwiek wartość przechowuje czwarta zmienna argumentu wiersza poleceń (w przykładowym użyciu „inny_arg”).

  5. Następnie zmienna przekazana do podprogramu jako zmienna zwracana ( curr_arg_value) jest testowana pod kątem niezdefiniowania, co mogłoby się zdarzyć, gdyby nie było aktualnie obliczanego argumentu wiersza poleceń. Początkowo było to porównanie wartości zmiennej zwracanej zawiniętej w nawiasy kwadratowe z pustymi nawiasami kwadratowymi (jest to jedyny znany mi sposób testowania argumentów programu lub podprogramu, który może zawierać cudzysłowy i był przeoczoną pozostałością po fazie prób i błędów), ale został od tego czasu naprawiony tak, jak jest teraz.


1
Chociaż ten kod może odpowiedzieć na pytanie, dostarczenie dodatkowego kontekstu dotyczącego tego, jak i / lub dlaczego rozwiązuje problem, poprawiłoby długoterminową wartość odpowiedzi.
— thewaywewewewewewbyed

@thewaywewere dziękuję, ciągle zapominam, że dla wielu osób (prawdopodobnie większości) lepiej jest mieć opis tekstowy zamiast kodu „oczywistego”.
— fen-fire,

@ fen-fire poza tym, że kod nie jest zrozumiały.
— Loduwijk

1

Ostatnia odpowiedź była dwa lata temu, ale potrzebowałem wersji na więcej niż dziewięć argumentów wiersza poleceń. Może być inny też ...

@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

Rozwiązanie to pierwsze 19 wierszy i konwertuje wszystkie argumenty na zmienne w stylu c. Wszystkie inne rzeczy po prostu sondują wynik i pokazują konwersję na lokalne argumenty. Możesz odwoływać się do argumentów według indeksu w dowolnej funkcji.


0

Solidnym rozwiązaniem jest delegowanie liczenia do podprogramu wywoływanego za pomocą call; podprogram używa gotoinstrukcji do emulacji pętli, w której shiftużywa się iteracyjnie argumentów (tylko podprogram):

@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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.