Fichiers batch - nombre d'arguments de ligne de commande

99

Il suffit de convertir certains scripts shell en fichiers batch et il y a une chose que je n'arrive pas à trouver ... et c'est un simple décompte du nombre d'arguments de ligne de commande.

par exemple. si tu as:

myapp foo bar

Dans Shell:

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

En lot

  • ?? -> 2 <---- quelle commande?!
  • % * -> foo bar
  • % 0 -> myapp
  • % 1 -> toto
  • % 2 -> barre

J'ai donc regardé autour de moi, et soit je regarde au mauvais endroit, soit je suis aveugle, mais je n'arrive pas à trouver un moyen d'obtenir le nombre d'arguments de ligne de commande transmis.

Existe-t-il une commande similaire à "$ #" du shell pour les fichiers batch?

ps. le plus proche que j'ai trouvé est d'itérer à travers les% 1 et d'utiliser 'shift', mais je dois me référer à% 1,% 2 etc. plus tard dans le script donc ce n'est pas bon.

pyko
la source
votre chaîne est 2 myapp foo bar?
PsychoData
2
conseil: ne convertissez pas sh en BAT. à la place, téléchargez cygwin et utilisez-le à la place. c'est-à-dire que vos scripts sh fonctionneront sur une machine Windows. et vous n'aurez pas à traduire chaque sh en BAT!
Marty McGowan

Réponses:

104

Googler un peu vous donne le résultat suivant des wikibooks :

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

echo %argC%

On dirait que cmd.exe a un peu évolué depuis l'ancien temps DOS :)

nimrodm
la source
7
Notez que cette variante de forne fonctionne que pour les arguments qui ressemblent à des noms de fichiers, pas pour les chaînes d'options telles que -?. L'utilisation de quotes ( for %%i in ("%*") ...) fonctionne pour l'argument comme -?mais échoue encore une fois pour les arguments entre guillemets en raison des guillemets imbriqués. Le seul moyen robuste semble impliquer shift...
Ferdinand Beyer
1
MyBatchFile "*.*" "Abc"rapporte que argC == 9. - Vote défavorable.
BrainSlugs83
J'ai testé cela avec 2500 arguments en tant que fonction et l'ai utilisé pour définir des tableaux de même taille. Ne me demandez pas pourquoi exactement. Surtout simplement apprendre de quoi le batch est capable.
T3RR0R le
44

Vous avez tendance à gérer le nombre d'arguments avec ce type de logique:

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

etc.

Si vous avez plus de 9 arguments, vous êtes foutu avec cette approche. Il existe divers hacks pour créer des compteurs que vous pouvez trouver ici , mais sachez que ce ne sont pas pour les timides.

Dean Povey
la source
6
Vous pouvez toujours shiftcompter plus de 9 ... et sans avoir 10 lignes de code de même apparence.
Joey
19

La fonction :getargcci-dessous peut être ce que vous recherchez.

@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

Il itère essentiellement une fois sur la liste (qui est locale à la fonction afin que les décalages n'affectent pas la liste dans le programme principal), en les comptant jusqu'à ce qu'elle s'épuise.

Il utilise également une astuce astucieuse, en passant le nom de la variable de retour à définir par la fonction.

Le programme principal illustre simplement comment l'appeler et fait écho aux arguments par la suite pour s'assurer qu'ils sont intacts:

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
la source
comment pouvez-vous modifier le flux en fonction de cela? (peut-être une question différente, mais je pense qu'un court exemple serait également très pratique ici!)
n611x007
@ n611x007 où echo Count is %argc%vous inséreriez votre propre code qui pourrait vérifier si ce décompte est correct, puis passer à autre chose.
kenny
7

Essaye ça:

SET /A ARGS_COUNT=0    
FOR %%A in (%*) DO SET /A ARGS_COUNT+=1    
ECHO %ARGS_COUNT%
David
la source
6
Cette réponse est-elle en quelque sorte différente de celle de @nimrod ? ...
bleuté
1
Vérifiez le commentaire de @FerdinandBeyer dans nimrodm's. je ne vais pas voter parce que vous avez 21 rep 8)
n611x007
6

Si le nombre d'arguments doit être un nombre exact (inférieur ou égal à 9), voici un moyen simple de le vérifier:

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
la source
2

Évite d'utiliser l'un shiftou l' autre ou un forcycle au détriment de la taille et de la lisibilité.

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

Production:

count_cmdline_args.bat testing more_testing arg3 another_arg

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

EDIT: Le "truc" utilisé ici implique:

  1. Construire une chaîne qui représente une variable d'argument de ligne de commande actuellement évaluée (c'est-à-dire "% 1", "% 2" etc.) en utilisant une chaîne qui contient un caractère de pourcentage ( %%) et une variable de compteur arg_idxà chaque itération de boucle.

  2. Stocker cette chaîne dans une variable curr_arg_label.

  3. Passer à la fois cette chaîne ( !curr_arg_label!) et le nom d'une variable de retour ( curr_arg_value) à un sous-programme primitif get_value.

  4. Dans le sous-programme, la valeur de son premier argument ( %1) est utilisée sur le côté gauche d'affectation ( set) et la valeur de son second argument ( %2) sur la droite. Cependant, lorsque l'argument du deuxième sous-programme est passé, il est résolu en valeur de l'argument de ligne de commande du programme principal par l'interpréteur de commandes. Autrement dit, ce qui est passé n'est pas, par exemple, "% 4" mais quelle que soit la valeur de la quatrième variable d'argument de ligne de commande ("another_arg" dans l'exemple d'utilisation).

  5. Ensuite, la variable donnée au sous-programme comme variable de retour ( curr_arg_value) est testée pour être indéfinie, ce qui se produirait si l'argument de ligne de commande actuellement évalué est absent. Au départ, il s'agissait d'une comparaison de la valeur de la variable de retour placée entre crochets et de crochets vides (ce qui est le seul moyen que je connaisse pour tester les arguments de programme ou de sous-programme qui peuvent contenir des guillemets et qui était un reste négligé de la phase d'essai et d'erreur) mais a depuis été fixé à ce qu'il est maintenant.

fen-feu
la source
1
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire sur la façon et / ou pourquoi il résout le problème améliorerait la valeur à long terme de la réponse.
thewaywewere
@thewaywewere merci, j'oublie toujours que pour beaucoup de gens (probablement la majorité), il vaut mieux avoir une description textuelle au lieu d'un code "explicite".
fen-fire
@ fen-fire sauf que le code n'est pas explicite.
Loduwijk
1

La dernière réponse remonte à deux ans maintenant, mais j'avais besoin d'une version pour plus de neuf arguments de ligne de commande. Peut-être qu'un autre le fait aussi ...

@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

La solution est les 19 premières lignes et convertit tous les arguments en variables dans un style c-like. Tous les autres éléments analysent simplement le résultat et affichent la conversion en arguments locaux. Vous pouvez référencer des arguments par index dans n'importe quelle fonction.

Kasimir
la source
0

Une solution robuste consiste à déléguer le comptage à un sous - programme appelé avec call; le sous-programme utilise des gotoinstructions pour émuler une boucle dans laquelle shiftest utilisé pour consommer les arguments (sous-programme uniquement) de manière itérative:

@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
la source