Archivos por lotes: número de argumentos de la línea de comandos

99

Simplemente convirtiendo algunos scripts de shell en archivos por lotes y hay una cosa que parece que no puedo encontrar ... y es un simple recuento del número de argumentos de la línea de comandos.

p.ej. si usted tiene:

myapp foo bar

En Shell:

  • $ # -> 2
  • $ * -> barra de foo
  • $ 0 -> myapp
  • $ 1 -> foo
  • $ 2 -> barra

En lote

  • ?? -> 2 <---- ¡¿Qué comando ?!
  • % * -> barra de foo
  • % 0 -> myapp
  • % 1 -> foo
  • % 2 -> barra

Así que miré a mi alrededor, y o estoy mirando en el lugar equivocado o estoy ciego, pero parece que no puedo encontrar una manera de obtener un recuento de la cantidad de argumentos de línea de comando pasados.

¿Existe un comando similar al "$ #" de shell para archivos por lotes?

PD. lo más cercano que he encontrado es iterar a través de% 1s y usar 'shift', pero necesito referir% 1,% 2, etc. más adelante en el script, así que eso no es bueno.

Pyko
fuente
tu cuerda es 2 myapp foo bar?
PsychoData
2
consejo: no convierta sh en BAT. en su lugar, descargue cygwin y utilícelo en su lugar. es decir, sus scripts sh funcionarán en una máquina con Windows. ¡y no tendrás que traducir cada sh a BAT!
Marty McGowan

Respuestas:

104

Buscar un poco en Google te da el siguiente resultado de wikilibros :

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

echo %argC%

Parece que cmd.exe ha evolucionado un poco desde los viejos tiempos de DOS :)

nimrodm
fuente
7
Tenga en cuenta que esta variante de forsolo funciona para argumentos que parecen nombres de archivos, no cadenas de opciones como -?. El uso de comillas ( for %%i in ("%*") ...) funciona para argumentos similares, -?pero nuevamente falla para los argumentos entre comillas debido a las comillas anidadas. La única forma sólida parece involucrar shift...
Ferdinand Beyer
1
MyBatchFile "*.*" "Abc"informa que argC == 9. - Downvoted.
BrainSlugs83
He probado esto con 2500 argumentos como función y lo he usado para definir matrices del mismo tamaño. No me preguntes por qué exactamente. Principalmente, solo estoy aprendiendo de qué es capaz el lote.
T3RR0R
44

Suele manejar varios argumentos con este tipo de lógica:

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

etc.

Sin embargo, si tiene más de 9 argumentos, entonces está jodido con este enfoque. Hay varios trucos para crear contadores que puede encontrar aquí , pero tenga en cuenta que no son para los pusilánimes.

Dean Povey
fuente
6
Todavía puede usar shiftpara contar más de 9 ... y sin tener 10 líneas de código de igual apariencia.
Joey
19

La función a :getargccontinuación puede ser lo que está buscando.

@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

Básicamente, itera una vez sobre la lista (que es local a la función, por lo que los cambios no afectarán la lista en el programa principal), contándolos hasta que se agote.

También utiliza un truco ingenioso, pasando el nombre de la variable de retorno que debe establecer la función.

El programa principal simplemente ilustra cómo llamarlo y luego hace eco de los argumentos para asegurarse de que no se modifiquen:

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
fuente
¿Cómo se puede alterar el flujo basándose en esto? (puede ser una pregunta diferente, ¡pero creo que un breve ejemplo también sería muy conveniente aquí!)
n611x007
@ n611x007 donde echo Count is %argc%insertaría su propio código que podría verificar si ese recuento es correcto y luego continuar.
Kenny
7

Prueba esto:

SET /A ARGS_COUNT=0    
FOR %%A in (%*) DO SET /A ARGS_COUNT+=1    
ECHO %ARGS_COUNT%
David
fuente
6
¿Es esta respuesta de alguna manera diferente de @nimrod uno ? ...
azulado
1
verifique el comentario de @FerdinandBeyer en nimrodm's. no voy a votar en contra porque tienes 21 rep 8)
n611x007
6

Si el número de argumentos debe ser un número exacto (menor o igual a 9), entonces esta es una forma sencilla de verificarlo:

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

Evita usar uno shifto un forciclo a costa del tamaño y la legibilidad.

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

Salida:

count_cmdline_args.bat testing more_testing arg3 another_arg

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

EDITAR: El "truco" utilizado aquí implica:

  1. Construir una cadena que represente una variable de argumento de línea de comandos evaluada actualmente (es decir, "% 1", "% 2", etc.) utilizando una cadena que contenga un carácter de porcentaje ( %%) y una variable de contador arg_idxen cada iteración del ciclo.

  2. Almacenar esa cadena en una variable curr_arg_label.

  3. Pasando tanto esa cadena ( !curr_arg_label!) como el nombre de una variable de retorno ( curr_arg_value) a un subprograma primitivo get_value.

  4. En el subprograma, el valor de su primer argumento ( %1) se usa en el lado izquierdo de la asignación ( set) y el valor de su segundo argumento ( %2) a la derecha. Sin embargo, cuando se pasa el argumento del segundo subprograma, el intérprete de comandos lo resuelve en el valor del argumento de la línea de comandos del programa principal. Es decir, lo que se pasa no es, por ejemplo, "% 4" sino cualquier valor que tenga la cuarta variable de argumento de la línea de comandos ("otro_arg" en el uso de muestra).

  5. Luego, la variable dada al subprograma como variable de retorno ( curr_arg_value) se prueba para no estar definida, lo que sucedería si el argumento de línea de comando evaluado actualmente no está presente. Inicialmente, esto era una comparación del valor de la variable de retorno envuelto entre corchetes con corchetes vacíos (que es la única forma que conozco de probar los argumentos del programa o subprograma que pueden contener comillas y fue un sobrante pasado por alto de la fase de prueba y error) pero desde entonces se ha arreglado como es ahora.

fen-fuego
fuente
1
Si bien este código puede responder a la pregunta, proporcionar un contexto adicional sobre cómo y / o por qué resuelve el problema mejoraría el valor de la respuesta a largo plazo.
thewaywewere
@thewaywewere gracias, sigo olvidando que para muchas personas (probablemente la mayoría) es mejor tener una descripción de texto en lugar de un código "autoexplicativo".
Fen-fire
@ fen-fire excepto que el código no se explica por sí mismo.
Loduwijk
1

La última respuesta fue hace dos años, pero necesitaba una versión para más de nueve argumentos de línea de comando. Puede ser que otro también lo haga ...

@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 solución son las primeras 19 líneas y convierte todos los argumentos a variables en un estilo similar a c. Todas las demás cosas simplemente sondean el resultado y muestran la conversión a argumentos locales. Puede hacer referencia a argumentos por índice en cualquier función.

Kasimir
fuente
0

Una solución sólida es delegar el conteo a una subrutina invocada con call; la subrutina usa gotodeclaraciones para emular un bucle en el que shiftse usa para consumir los argumentos (solo de subrutina) de manera iterativa:

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