Batch-Dateien - Anzahl der Befehlszeilenargumente

99

Wenn Sie nur einige Shell-Skripte in Batch-Dateien konvertieren, gibt es eine Sache, die ich anscheinend nicht finden kann ... und das ist eine einfache Zählung der Anzahl der Befehlszeilenargumente.

z.B. Wenn Sie haben:

myapp foo bar

In Shell:

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

Im Batch

  • ?? -> 2 <---- welcher Befehl?!
  • % * -> foo bar
  • % 0 -> myapp
  • % 1 -> foo
  • % 2 -> bar

Ich habe mich also umgesehen und entweder an der falschen Stelle gesucht oder bin blind, aber ich kann anscheinend keinen Weg finden, um die Anzahl der übergebenen Befehlszeilenargumente zu ermitteln.

Gibt es einen Befehl ähnlich dem "$ #" der Shell für Batch-Dateien?

ps. Das nächste, was ich gefunden habe, ist, die% 1s zu durchlaufen und 'shift' zu verwenden, aber ich muss% 1,% 2 usw. später im Skript erneut referenzieren, damit das nicht gut ist.

pyko
quelle
deine Saite ist 2 myapp foo bar?
PsychoData
2
Hinweis: Konvertieren Sie sh nicht in BAT. Laden Sie stattdessen cygwin herunter und verwenden Sie es stattdessen. dh Ihre sh-Skripte funktionieren auf einem Windows-Computer. und du musst nicht jeden sh in BAT übersetzen!
Marty McGowan

Antworten:

104

Wenn Sie ein bisschen googeln, erhalten Sie das folgende Ergebnis von Wikibooks :

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

echo %argC%

Scheint, als hätte sich cmd.exe ein bisschen aus den alten DOS-Tagen entwickelt :)

Nimrodm
quelle
7
Beachten Sie, dass diese Variante fornur für Argumente funktioniert, die wie Dateinamen aussehen, nicht für Optionszeichenfolgen wie -?. Die Verwendung von Zitaten ( for %%i in ("%*") ...) funktioniert für Argumente wie -?, schlägt jedoch für Argumente in Anführungszeichen aufgrund verschachtelter Anführungszeichen fehl. Der einzige robuste Weg scheint zu beinhalten shift...
Ferdinand Beyer
1
MyBatchFile "*.*" "Abc"berichtet, dass argC == 9. - Downvoted.
BrainSlugs83
Habe dies auf 2500 Argumente als Funktion getestet und damit Arrays gleicher Größe definiert. Frag mich nicht warum genau. Meistens nur lernen, wozu Batch fähig ist.
T3RR0R
43

Sie neigen dazu, die Anzahl der Argumente mit dieser Art von Logik zu behandeln:

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

etc.

Wenn Sie mehr als 9 Argumente haben, sind Sie jedoch mit diesem Ansatz fertig. Es gibt verschiedene Hacks zum Erstellen von Zählern, die Sie hier finden , aber seien Sie gewarnt, diese sind nichts für schwache Nerven.

Dean Povey
quelle
6
Sie können immer noch shiftmehr als 9 zählen ... und ohne 10 Zeilen gleich aussehenden Codes.
Joey
18

Die folgende Funktion ist :getargcmöglicherweise genau das, wonach Sie suchen.

@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

Grundsätzlich wird die Liste einmal durchlaufen (was für die Funktion lokal ist, damit die Verschiebungen die Liste im Hauptprogramm nicht beeinflussen) und gezählt, bis sie abgelaufen ist.

Es wird auch ein raffinierter Trick verwendet, bei dem der Name der von der Funktion festzulegenden Rückgabevariablen übergeben wird.

Das Hauptprogramm zeigt nur, wie es aufgerufen wird, und gibt die Argumente anschließend wieder, um sicherzustellen, dass sie unberührt bleiben:

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
quelle
Wie können Sie den Fluss basierend darauf ändern? (mag eine andere Frage sein, aber ich denke, ein kurzes Beispiel wäre auch hier sehr praktisch!)
n611x007
@ n611x007 wo echo Count is %argc%Sie Ihren eigenen Code einfügen würden, der prüfen könnte, ob diese Zählung korrekt ist, und dann weitermachen .
Kenny
7

Versuche dies:

SET /A ARGS_COUNT=0    
FOR %%A in (%*) DO SET /A ARGS_COUNT+=1    
ECHO %ARGS_COUNT%
David
quelle
6
ist diese Antwort irgendwie anders als @nimrod eine ? ...
bläulich
1
Überprüfen Sie den Kommentar von @FerdinandBeyer in Nimrodm's. Ich werde nicht abstimmen, weil du 21 Wiederholungen hast. 8)
n611x007
6

Wenn die Anzahl der Argumente genau sein sollte (kleiner oder gleich 9), können Sie dies auf einfache Weise überprüfen:

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
quelle
2

Vermeidet die Verwendung von entweder shiftoder eines forZyklus auf Kosten der Größe und Lesbarkeit.

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

Ausgabe:

count_cmdline_args.bat testing more_testing arg3 another_arg

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

EDIT: Der hier verwendete "Trick" beinhaltet:

  1. Erstellen einer Zeichenfolge, die eine aktuell ausgewertete Befehlszeilenargumentvariable (dh "% 1", "% 2" usw.) darstellt, unter Verwendung einer Zeichenfolge, die bei jeder Schleifeniteration ein Prozentzeichen ( %%) und eine arg_idxZählervariable enthält.

  2. Speichern dieser Zeichenfolge in einer Variablen curr_arg_label.

  3. Übergeben Sie sowohl diesen string ( !curr_arg_label!) als auch den Namen ( curr_arg_value) einer Rückgabevariablen an ein primitives Unterprogramm get_value.

  4. Im Unterprogramm wird der %1Wert des ersten Arguments ( ) auf der linken Seite von assign ( set) und der %2Wert des zweiten Arguments ( ) auf der rechten Seite verwendet. Wenn jedoch das Argument des zweiten Unterprogramms übergeben wird, wird es vom Befehlsinterpreter in den Wert des Befehlszeilenarguments des Hauptprogramms aufgelöst. Das heißt, was übergeben wird, ist beispielsweise nicht "% 4", sondern der Wert, den die vierte Befehlszeilenargumentvariable enthält ("another_arg" in der Beispielverwendung).

  5. Anschließend wird die dem Unterprogramm als Rückgabevariable ( curr_arg_value) gegebene Variable auf Undefiniert getestet. Dies würde passieren, wenn das aktuell ausgewertete Befehlszeilenargument fehlt. Anfänglich war dies ein Vergleich des Werts der Rückgabevariablen in eckigen Klammern mit leeren eckigen Klammern (dies ist die einzige Möglichkeit, wie ich Programm- oder Unterprogrammargumente kenne, die Anführungszeichen enthalten können und ein Überbleibsel aus der Trial-and-Error-Phase waren) wurde da fixiert wie es jetzt ist.

Fen-Fire
quelle
1
Während dieser Code die Frage beantworten kann, würde die Bereitstellung eines zusätzlichen Kontexts darüber, wie und / oder warum er das Problem löst, den langfristigen Wert der Antwort verbessern.
thewaywewere
@thewaywewere danke, ich vergesse immer wieder, dass es für viele Menschen (wahrscheinlich die Mehrheit) besser ist, eine Textbeschreibung anstelle eines "selbsterklärenden" Codes zu haben.
Fen-Fire
@ Fen-Fire, außer dass der Code nicht selbsterklärend ist.
Loduwijk
1

Die letzte Antwort war vor zwei Jahren, aber ich brauchte eine Version für mehr als neun Befehlszeilenargumente. Vielleicht macht auch ein anderer ...

@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

Die Lösung besteht aus den ersten 19 Zeilen und konvertiert alle Argumente in Variablen in einem c-ähnlichen Stil. Alle anderen Dinge prüfen nur das Ergebnis und zeigen die Konvertierung in lokale Argumente. Sie können Argumente in jeder Funktion nach Index referenzieren.

Kasimir
quelle
0

Eine robuste Lösung besteht darin, das Zählen an eine Unterroutine zu delegieren, die mit aufgerufen wird call. Die Unterroutine verwendet gotoAnweisungen, um eine Schleife zu emulieren, in der die shiftArgumente (nur Unterroutine) iterativ verwendet werden:

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