Obtenga la lista de argumentos pasados ​​en el script por lotes de Windows (.bat)


406

Me gustaría encontrar una contraparte por lotes de Windows para Bash $@que contenga una lista de todos los argumentos pasados ​​a un script.

¿O tengo que molestarme shift?

Respuestas:


622

dancavallaro tiene razón, %*para todos los parámetros de la línea de comandos (excluyendo el nombre del script). También puede encontrar estos útiles:

%0- el comando utilizado para llamar al archivo por lotes (podría ser foo, ..\foo, c:\bats\foo.bat, etc.)
%1es el primer parámetro de línea de comandos,
%2es el segundo parámetro de línea de comandos,
y así sucesivamente hasta %9(y SHIFTse puede utilizar para aquellos que después del 9).

%~nx0- el nombre real del archivo por lotes, independientemente del método de llamada (some-batch.bat)
%~dp0- unidad y ruta al script (d: \ scripts)
%~dpnx0- es el nombre de ruta completo del script (d: \ scripts \ some -batch.bat)

Más ejemplos de información en https://www.ss64.com/nt/syntax-args.html y https://www.robvanderwoude.com/parameters.html


11
Tenga en cuenta que SHIFT no afecta a% *, lo que hace que sea imposible usar SHIFT [/ n] para algo intuitivo como acceder a la línea de comando completa a partir del parámetro n-ésimo.
Van Jone

44
Algo más que descubrí es que %~dpn0devolverá la unidad y la ruta al script más el nombre del archivo por lotes, pero no la extensión. Encontré esto particularmente útil al crear scripts por lotes que llaman a un .ps1archivo con el mismo nombre. Esencialmente, se drefiere a la unidad, se prefiere a la ruta, se nrefiere al nombre del archivo y se xrefiere a la extensión. Con esa información, puedes hacer bastante.
Dan Atkinson

Por curiosidad, suponiendo que quiero usar ECHO para imprimir la ruta del archivo pero reemplazando su extensión, ¿cómo sería?
Matheus Rocha

2
@MatheusRocha@echo %~n0.my_ext
matt wilkie

@mattwilkie Gracias, lo descubrí usando ECHO para ver qué imprimiría como resultado.
Matheus Rocha

150

%* parece contener todos los argumentos pasados ​​al script.


¿Alguna idea de por qué no maneja algunos caracteres como &? Cuando intento recuperar varios la ruta de varios archivos seleccionados (usando el menú Enviar a y "% *" que pasa los argumentos a un .py) detiene la lista en los caracteres &. Entonces, si, por ejemplo, el segundo archivo contiene un &en su nombre, &se omitirá la parte del nombre después del y los archivos restantes.)
JinSnow

1
@JinSnow The &es un personaje especial que significa stop this command and start another one. Se & debe citar un nombre de archivo que contenga un .
Jesse Chisholm

65

%1... %ny %*contiene los argumentos, pero puede ser complicado acceder a ellos, porque el contenido será interpretado.
Por lo tanto, es imposible manejar algo como esto con declaraciones normales

myBatch.bat "&"^&

Cada línea falla, ya que cmd.exe intenta ejecutar uno de los símbolos (el contenido de% 1 es "&"&)

set var=%1
set "var=%1"
set var=%~1
set "var=%~1"

Pero existe una solución alternativa con un archivo temporal

@echo off
SETLOCAL DisableDelayedExpansion

SETLOCAL
for %%a in (1) do (
    set "prompt=$_"
    echo on
    for %%b in (1) do rem * #%1#
    @echo off
) > param.txt
ENDLOCAL

for /F "delims=" %%L in (param.txt) do (
  set "param1=%%L"
)
SETLOCAL EnableDelayedExpansion
set "param1=!param1:*#=!"
set "param1=!param1:~0,-2!"
echo %%1 is '!param1!'

El truco es habilitar echo ony expandir %1después de una remdeclaración (también funciona con% 2 ..% *).
Pero para poder redirigir la salida de echo on, necesita los dos FOR-LOOPS.

Los caracteres adicionales * #se usan para estar seguros contra contenidos como /?(mostraría la ayuda para REM).
O un símbolo de intercalación al final de la línea podría funcionar como un carácter multilínea.

El FOR / F debe funcionar con la expansión retrasada desactivada, de lo contrario el contenido con "!" Sería destruido.
Después de eliminar los caracteres adicionales param1y lo tienes.

Y para usar param1de manera segura, habilite la expansión demorada.

Editar: un comentario para% 0

%0contiene el comando utilizado para llamar al lote, también conserva el caso como en FoO.BaT
Pero después de una llamada a una función %0y también %~0contiene el nombre de la función (o mejor, la cadena que se utilizó para llamar a la función).
Pero %~f0contigo aún puedes recordar el nombre del archivo.

@echo off
echo main %0, %~0, %~f0
call :myLabel+xyz
exit /b

:MYlabel
echo func %0, %~0, %~f0
exit /b

Salida

main test.bat, test.bat, C:\temp\test.bat
func :myLabel+xyz, :myLabel+xyz, C:\temp\test.bat

66
+++ 1 OMG: el %~f0truco es totalmente inesperado, genial y potencialmente muy útil :-) No es sorprendente que los otros modificadores funcionen de la misma manera, siempre operando en el archivo por lotes principal, incluso cuando están en una subrutina llamada. Esto parece digno de sus propias preguntas y respuestas: "¿Cómo acceder al nombre del lote en ejecución en una subrutina llamada?"
dbenham

Si no recuerdo mal, puede usar call con argumentos, y estos se almacenarán en% 1,% 2, ...% n como cuando ejecuta un script.
Nulano

49

Encontré eso la próxima vez que necesite buscar esta información. En lugar de abrir un navegador y buscarlo en Google, puede escribir call /?su cmd y lo obtendrá:

...

%* in a batch script refers to all the arguments (e.g. %1 %2 %3
    %4 %5 ...)

Substitution of batch parameters (%n) has been enhanced.  You can
now use the following optional syntax:

    %~1         - expands %1 removing any surrounding quotes (")
    %~f1        - expands %1 to a fully qualified path name
    %~d1        - expands %1 to a drive letter only
    %~p1        - expands %1 to a path only
    %~n1        - expands %1 to a file name only
    %~x1        - expands %1 to a file extension only
    %~s1        - expanded path contains short names only
    %~a1        - expands %1 to file attributes
    %~t1        - expands %1 to date/time of file
    %~z1        - expands %1 to size of file
    %~$PATH:1   - searches the directories listed in the PATH
                   environment variable and expands %1 to the fully
                   qualified name of the first one found.  If the
                   environment variable name is not defined or the
                   file is not found by the search, then this
                   modifier expands to the empty string

The modifiers can be combined to get compound results:

    %~dp1       - expands %1 to a drive letter and path only
    %~nx1       - expands %1 to a file name and extension only
    %~dp$PATH:1 - searches the directories listed in the PATH
                   environment variable for %1 and expands to the
                   drive letter and path of the first one found.
    %~ftza1     - expands %1 to a DIR like output line

In the above examples %1 and PATH can be replaced by other
valid values.  The %~ syntax is terminated by a valid argument
number.  The %~ modifiers may not be used with %*

20

La forma de recuperar todos los argumentos de un script está aquí:

@ECHO off
ECHO The %~nx0 script args are...
for %%I IN (%*) DO ECHO %%I
pause

2
En realidad no, %%Iinterpretate y puede romper tu script.
Boris Brodski

5

Aquí hay una manera bastante simple de obtener los argumentos y configurarlos como envoltorios. En este ejemplo, me referiré a ellos como claves y valores.

Guarde el siguiente ejemplo de código como "args.bat". Luego llame al archivo por lotes que guardó desde una línea de comando. ejemplo: arg.bat --x 90 --y 120

He proporcionado algunos comandos de eco para guiarlo a través del proceso. Pero el resultado final es que --x tendrá un valor de 90 y --y tendrá un valor de 120 (es decir, si ejecuta el ejemplo como se especificó anteriormente ;-)).

A continuación, puede usar la instrucción condicional 'si está definida' para determinar si ejecutar o no su bloque de código. Entonces, digamos ejecutar: "arg.bat --x hello-world" Entonces podría usar la declaración "IF DEFINED --x echo% - x%" y los resultados serían "hello-world". Debería tener más sentido si ejecuta el lote.

@setlocal enableextensions enabledelayedexpansion
@ECHO off
ECHO.
ECHO :::::::::::::::::::::::::: arg.bat example :::::::::::::::::::::::::::::::
ECHO :: By:      User2631477, 2013-07-29                                   ::
ECHO :: Version: 1.0                                                         ::
ECHO :: Purpose: Checks the args passed to the batch.                        ::
ECHO ::                                                                      ::
ECHO :: Start by gathering all the args with the %%* in a for loop.          ::
ECHO ::                                                                      ::
ECHO :: Now we use a 'for' loop to search for our keys which are identified  ::
ECHO :: by the text '--'. The function then sets the --arg ^= to the next    ::
ECHO :: arg. "CALL:Function_GetValue" ^<search for --^> ^<each arg^>         ::
ECHO ::                                                                      ::
ECHO ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

ECHO.

ECHO ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
ECHO :: From the command line you could pass... arg.bat --x 90 --y 220       ::
ECHO ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
ECHO.
ECHO.Checking Args:"%*"

FOR %%a IN (%*) do (
    CALL:Function_GetValue "--","%%a" 
)

ECHO.
ECHO ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
ECHO :: Now lets check which args were set to variables...                   ::
ECHO ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
ECHO.
ECHO ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
ECHO :: For this we are using the CALL:Function_Show_Defined "--x,--y,--z"   ::
ECHO ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
ECHO.
CALL:Function_Show_Defined "--x,--y,--z"
endlocal
goto done

:Function_GetValue

REM First we use find string to locate and search for the text.
echo.%~2 | findstr /C:"%~1" 1>nul

REM Next we check the errorlevel return to see if it contains a key or a value
REM and set the appropriate action.

if not errorlevel 1 (
  SET KEY=%~2
) ELSE (
  SET VALUE=%~2
)
IF DEFINED VALUE (
    SET %KEY%=%~2
    ECHO.
    ECHO ::::::::::::::::::::::::: %~0 ::::::::::::::::::::::::::::::
    ECHO :: The KEY:'%KEY%' is now set to the VALUE:'%VALUE%'                     ::
    ECHO :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    ECHO.
    ECHO %KEY%=%~2
    ECHO.
    REM It's important to clear the definitions for the key and value in order to
    REM search for the next key value set.
    SET KEY=
    SET VALUE=
)
GOTO:EOF

:Function_Show_Defined 
ECHO.
ECHO ::::::::::::::::::: %~0 ::::::::::::::::::::::::::::::::
ECHO :: Checks which args were defined i.e. %~2
ECHO :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
ECHO.
SET ARGS=%~1
for %%s in (%ARGS%) DO (
    ECHO.
    ECHO :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    ECHO :: For the ARG: '%%s'                         
    IF DEFINED %%s (
        ECHO :: Defined as: '%%s=!%%s!'                                             
    ) else (
        ECHO :: Not Defined '%%s' and thus has no value.
    )
    ECHO :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    ECHO.
)
goto:EOF

:done

¡Esto es muy útil!
robe007

3

ingrese la descripción de la imagen aquí

Para usar el bucle para obtener todos los argumentos y en lote puro:

Obs: Para usar sin:?*&<>


@echo off && setlocal EnableDelayedExpansion

for %%Z in (%*)do set "_arg_=%%Z" && set/a "_cnt+=1+0" && call set "_arg_[!_cnt!]=!_arg_!")

:: write/test these arguments/parameters ::
for /l %%l in (1 1 !_cnt!)do echo/ The argument n:%%l is: !_arg_[%%l]!

goto :eof 

Su código está listo para hacer algo con el número de argumento donde lo necesita, como ...

@echo off && setlocal EnableDelayedExpansion

for %%Z in (%*)do set "_arg_=%%Z" && set/a "_cnt+=1+0" && call set "_arg_[!_cnt!]=!_arg_!"

echo= !_arg_[1]! !_arg_[2]! !_arg_[2]!> log.txt

2

El siguiente código simula una matriz (' params'): toma los parámetros recibidos por el script y los almacena en las variables params_1.. params_n, donde n= params_0= el número de elementos de la matriz:

@echo off

rem Storing the program parameters into the array 'params':
rem Delayed expansion is left disabled in order not to interpret "!" in program parameters' values;
rem however, if a parameter is not quoted, special characters in it (like "^", "&", "|") get interpreted at program launch
set /a count=0
:repeat
    set /a count+=1
    set "params_%count%=%~1"
    shift
    if defined params_%count% (
        goto :repeat
    ) else (
        set /a count-=1
    )    
set /a params_0=count

rem Printing the program parameters stored in the array 'params':
rem After the variables params_1 .. params_n are set with the program parameters' values, delayed expansion can
rem be enabled and "!" are not interpreted in the variables params_1 .. params_n values
setlocal enabledelayedexpansion
    for /l %%i in (1,1,!params_0!) do (
        echo params_%%i: "!params_%%i!"
    )
endlocal

pause
goto :eof

1

Si tiene parámetros entre comillas que contienen espacios, %*no funcionará correctamente. La mejor solución que encontré es tener un bucle que une todos los argumentos: https://serverfault.com/a/22541

set args=%1
shift
:start
if [%1] == [] goto done
set args=%args% %1
shift
goto start

:done
(use %args% here)

1

Puede usar ForCommad, para obtener la lista de Arg.

Ayuda: For /? Ayuda:Setlocal /?

Aquí está mi camino =

@echo off
::For Run Use This = cmd /c ""Args.cmd" Hello USER Scientist etc"
setlocal EnableDelayedExpansion
set /a Count=0
for %%I IN (%*) DO (
 Echo Arg_!Count! = %%I
 set /a Count+=1 
)
Echo Count Of Args = !Count!
Endlocal

No necesita el comando Shift.


0
@echo off
:comienzo

:: Inserte su código aquí
echo. %% 1 es ahora:% ~ 1
:: Finaliza inserta tu código aquí

si "% ~ 2" NEQ "" (
    cambio
    goto: inicio
)

0

Mucha de la información anterior me llevó a una mayor investigación y, en última instancia, a mi respuesta, por lo que quería contribuir con lo que terminé haciendo con la esperanza de que ayude a alguien más:

  • También quería pasar un número variado de variables a un archivo por lotes para que pudieran procesarse dentro del archivo.

  • Estaba de acuerdo con pasarlos al archivo por lotes usando comillas

  • Me gustaría que se procesen de forma similar a la siguiente, pero usando un bucle en lugar de escribirlo manualmente:

Entonces quería ejecutar esto:

prog_ZipDeleteFiles.bat "_appPath=C:\Services\Logs\PCAP" "_appFile=PCAP*.?"

Y a través de la magia de for loops, haz esto dentro del archivo por lotes:

set "_appPath=C:\Services\Logs\PCAP"
set "_appFile=PCAP*.?"

El problema que tenía es que todos mis intentos de usar un bucle for no funcionaban. El siguiente ejemplo:

for /f "tokens* delims= " in %%A (%*) DO (
   set %%A
)

solo haría:

set "_appPath=C:\Services\Logs\PCAP"

y no:

set "_appPath=C:\Services\Logs\PCAP"
set "_appFile=PCAP*.?"

incluso después de configurar

SETLOCAL EnableDelayedExpansion

La razón fue que el ciclo for leyó toda la línea y asignó mi segundo parámetro a %% B durante la primera iteración del ciclo. Debido a que% * representa todos los argumentos, solo hay una sola línea para procesar; por lo tanto, solo ocurre una pasada del ciclo for. Esto es así por diseño, y mi lógica estaba equivocada.

Así que dejé de intentar usar un bucle for y simplifiqué lo que estaba tratando de hacer usando if, shift y una instrucción goto. Estuve de acuerdo en que es un poco hack pero es más adecuado para mis necesidades. Puedo recorrer todos los argumentos y luego usar una instrucción if para abandonar el ciclo una vez que los procese todos.

La declaración ganadora de lo que estaba tratando de lograr:

echo on
:processArguments
:: Process all arguments in the order received
if defined %1 then (
    set %1
    shift
    goto:processArguments
) ELSE (
    echo off 
)

ACTUALIZACIÓN: en su lugar, tuve que modificar lo siguiente, estaba exponiendo todas las variables de entorno al intentar hacer referencia a% 1 y usar shift de esta manera:

echo on
shift
:processArguments
:: Process all arguments in the order received
if defined %0 then (
    set %0
    shift
    goto:processArguments
) ELSE (
    echo off 
)

Estoy equivocado, hice más comprobaciones y después de la tercera iteración, mi comando shift expone todas las variables de entorno, lo que rompe lo anterior. Todavía estoy investigando si hay alguna forma de evitarlo.
John Ahearn

Tuve que hacer una modificación, en lugar de usar% 1, tuve que cambiar una vez inicialmente y usar% 0 o de lo contrario me encontré con un problema extraño donde eventualmente% 1 contenía todas las variables de entorno actuales.
John Ahearn

0

A veces necesito extraer varios parámetros de la línea de comandos, pero no todos y no del primero. Asumamos la siguiente llamada:

Test.bat uno dos tres cuatro cinco seis siete

Por supuesto, la extensión ".bat" no es necesaria, al menos tiene test.exe en la misma carpeta. Lo que queremos es obtener la salida "tres cuatro cinco" (no importa si una línea o tres). El objetivo es que solo necesito tres parámetros y comenzar en el tercero. Para tener un ejemplo simple, la acción para cada uno es simplemente "eco", pero puede pensar en alguna otra acción más compleja sobre los parámetros seleccionados.

He producido algunos ejemplos (opciones) para que pueda ver cuál considera mejor para usted. Desafortunadamente, no hay una buena manera (o no sé) basada en un bucle for en el rango como "for %% i in (% 3, 1,% 5)".

@echo off 
setlocal EnableDelayedExpansion

echo Option 1: one by one (same line)
echo %3, %4, %5
echo.

echo Option 2: Loop For one by one
for %%a in (%3, %4, %5) do echo %%a
echo.

echo Option 3: Loop For with check of limits
set i=0
for %%a in (%*) do (
    set /A i=i+1
    If !i! GTR 2 if !i! LSS 6 echo %%a
)
echo.

echo Option 4: Loop For with auxiliary list
for /l %%i in (3,1,5) do  (
    set a=%%i
    set b=echo %%
    set b=!b!!a!
    call !b!
)
echo.

echo Option 5: Assigning to an array of elements previously
set e[0]=%0
set i=0 
for %%a in (%*) do (
    set /A i=i+1
    set e[!i!]=%%a
)
for  /l %%i in (3,1,5) do (
    echo !e[%%i]!
)
echo.

echo Option 6: using shift and goto loop. It doesn't work with for loop
set i=2
:loop6
    set /A i=i+1
    echo %3
    shift
    If %i% LSS 5 goto :loop6

Probablemente pueda encontrar más opciones o combinar varias. Disfrútenlos


-1

Versión de Windows (aunque necesita socat)

C:\Program Files (x86)\Git\bin>type gitproxy.cmd
socat STDIO PROXY:proxy.mycompany.de:%1:%2,proxyport=3128

configurarlo:

C:\Users\exhau\AppData\Roaming\npm>git config --global core.gitproxy gitproxy.cmd
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.