¿Cómo obtengo el resultado de un comando en una variable en Windows?

132

Estoy buscando obtener el resultado de un comando como una variable en un script por lotes de Windows (vea cómo obtener el resultado de un comando en bash para el equivalente de script de bash). Se prefiere una solución que funcione en un archivo .bat, pero también son bienvenidas otras soluciones comunes de secuencias de comandos de Windows.

John Meagher
fuente
9
John, es ridículamente difícil encontrar esta pregunta muy útil. ¿Podría considerar agregar frases alternativas como Cómo capturar la salida de un programa en una variable en un archivo por lotes de Windows?
Piotr Dobrogost
3
Google mostró esta página en el tercer lugar.
René Nyffenegger
1
Posible duplicado de la asignación por lotes
Michael Freidgeim
@MichaelFreidgeim esta pregunta en realidad se hizo 2 años antes de ese duplicado, pero hay múltiples duplicados de esta pregunta. Vea los comentarios en stackoverflow.com/questions/6359820/…
icc97
1
@ icc97, "Posible duplicado" es una forma de limpiar: cerrar preguntas similares y mantener una con las mejores respuestas. La fecha no es esencial. Consulte meta.stackexchange.com/questions/147643/…. Si acepta que requiere una aclaración, vote en meta.stackexchange.com/questions/281980/…. Si ve varios duplicados, debe elegir uno como canónico y votar para cerrar otros. .
Michael Freidgeim

Respuestas:

34

Si tiene que capturar toda la salida del comando, puede usar un lote como este:

@ECHO OFF
IF NOT "%1"=="" GOTO ADDV
SET VAR=
FOR /F %%I IN ('DIR *.TXT /B /O:D') DO CALL %0 %%I
SET VAR
GOTO END

:ADDV
SET VAR=%VAR%!%1

:END

Todas las líneas de salida se almacenan en VAR separadas con "!".

@John: ¿hay algún uso práctico para esto? Creo que debería ver PowerShell o cualquier otro lenguaje de programación capaz de realizar tareas de secuencias de comandos fácilmente (Python, Perl, PHP, Ruby)

PabloG
fuente
La respuesta de una sola línea resolverá el caso específico que tengo. Solo pensé que había una opción de varias líneas (o una opción más sencilla de una sola línea). Supongo que las capacidades de los archivos bat no son tan geniales. La necesidad es que los scripts de murciélago envuelvan aplicaciones Java. Construyendo rutas de clase principalmente.
John Meagher
Ten mucho cuidado con eso. GWT también trató de usar archivos por lotes para iniciar Java con rutas de clases específicas, que fallaron tan pronto como llegaste a directorios con caracteres no ASCII (en ese caso, era mi casa :)). Desde entonces reescribieron eso con VBS.
Joey
25
¿Hay algún uso práctico para esto? ¿Estás bromeando? Esto se usa todo el tiempo en otros shells (supongo que todos los GNU / Linux) y es muy útil.
Piotr Dobrogost
1
@PiotrDobrogost Creo que lo que estaba tratando de decir era que los scripts de bash son una reliquia horrible del pasado (¿Necesitas usar un bucle for solo para capturar la salida de un programa? ¿En serio?), Y si alguna vez llegas al punto donde necesites usar variables, deberías usar algo más moderno como PowerShell.
Ajedi32
3
Hay un uso práctico, en realidad ... un poco. Quiero usar un script de PowerShell para encontrar una ruta de Visual Studio en particular, pero lo que necesito de esa ruta es un archivo por lotes que establece un montón de env para que pueda llamar a editbin ... Entonces necesito encontrar la ruta y devolverlo a la instancia de llamada de cmdpara que pueda ejecutarlo allí. ... Sí, esto es tan terrible como los sonidos. Visual Studio me da ganas de llorar a veces. @ Ajedi32 Creo que querías decir que los scripts por lotes son una reliquia horrible del pasado. ;)
jpmc26
85

El humilde de comando ha acumulado algunas capacidades interesantes a lo largo de los años:

D:\> FOR /F "delims=" %i IN ('date /t') DO set today=%i
D:\> echo %today%
Sat 20/09/2008

Tenga en cuenta que "delims="sobrescribe el espacio predeterminado y los delimitadores de tabulación para que la salida del comando de fecha se engulle de una vez.

Para capturar la salida multilínea, todavía puede ser esencialmente una línea (usando la variable lf como delimitador en la variable resultante):

REM NB:in a batch file, need to use %%i not %i
setlocal EnableDelayedExpansion
SET lf=-
FOR /F "delims=" %%i IN ('dir \ /b') DO if ("!out!"=="") (set out=%%i) else (set out=!out!%lf%%%i)
ECHO %out%

Para capturar una expresión canalizada, use ^|:

FOR /F "delims=" %%i IN ('svn info . ^| findstr "Root:"') DO set "URL=%%i"
tarda
fuente
1
Al igual que con la respuesta de @PabloG, esto solo funcionará para obtener la última línea de salida del comando, "fecha / t" en este caso.
John Meagher
11
Encontré su respuesta para trabajar solo cuando usé signos de porcentaje doble, es decirFOR /F "delims=" %%i IN ('date /t') DO set today=%%i
Rabarberski
18
@Rabarberski: Si escribe un forcomando directamente en la línea de comando, usa uno simple %. Si lo usa en un archivo por lotes, lo usa %%.
indiv
@tardate: ¿Podría decirnos cómo se verá el script si el comando necesita pasar un argumento con %, por ejemplo:for /f "delims=" %%i in ('date +%F_%H-%M-%S') do set now=%%i
dma_k
1
@degenerate El comando es válido siempre que el datecomando utilizado provenga de Cygwin. Estoy de acuerdo en que debería haber mencionado eso.
dma_k
24

Para obtener el directorio actual, puede usar esto:

CD > tmpFile
SET /p myvar= < tmpFile
DEL tmpFile
echo test: %myvar%

Sin embargo, está usando un archivo temporal, por lo que no es el más bonito, ¡pero ciertamente funciona! 'CD' pone el directorio actual en 'tmpFile', 'SET' carga el contenido de tmpFile.

Aquí hay una solución para varias líneas con "matriz":

@echo off

rem ---------
rem Obtain line numbers from the file
rem ---------

rem This is the file that is being read: You can replace this with %1 for dynamic behaviour or replace it with some command like the first example i gave with the 'CD' command.
set _readfile=test.txt

for /f "usebackq tokens=2 delims=:" %%a in (`find /c /v "" %_readfile%`) do set _max=%%a
set /a _max+=1
set _i=0
set _filename=temp.dat

rem ---------
rem Make the list
rem ---------

:makeList
find /n /v "" %_readfile% >%_filename%

rem ---------
rem Read the list
rem ---------

:readList
if %_i%==%_max% goto printList

rem ---------
rem Read the lines into the array
rem ---------
for /f "usebackq delims=] tokens=2" %%a in (`findstr /r "\[%_i%]" %_filename%`) do set _data%_i%=%%a
set /a _i+=1
goto readList

:printList
del %_filename%
set _i=1
:printMore
if %_i%==%_max% goto finished
set _data%_i%
set /a _i+=1
goto printMore

:finished

Pero es posible que desee considerar mudarse a otro shell más potente o crear una aplicación para estas cosas. Extiende bastante las posibilidades de los archivos por lotes.

Actividad malvada
fuente
¿Hay alguna variación que funcione para capturar varias líneas del comando?
John Meagher
66
Para obtener el directorio actual, puede usar echo% CD%
Joe
9

necesita usar el SETcomando con parámetro /Py dirigir su salida a él. Por ejemplo, consulte http://www.ss64.com/nt/set.html . Funcionará para CMD, no estoy seguro acerca de los archivos .BAT

De un comentario a esta publicación:

Ese enlace tiene el comando " Set /P _MyVar=<MyFilename.txt" que dice que se establecerá _MyVaren la primera línea desde MyFilename.txt. Esto podría usarse como " myCmd > tmp.txt" con " set /P myVar=<tmp.txt". Pero solo obtendrá la primera línea de salida, no toda la salida

Ilya Kochetov
fuente
2
Ese enlace tiene el comando "Set / P _MyVar = <MyFilename.txt" que dice que establecerá _MyVar en la primera línea de MyFilename.txt. Esto podría usarse como "myCmd> tmp.txt" con "set / P myVar = <tmp.txt". Pero solo obtendrá la primera línea de salida, no toda la salida.
John Meagher
Esta respuesta es para principiantes como yo
5

Ejemplo para establecer en la variable de entorno "V" el archivo más reciente

FOR /F %I IN ('DIR *.* /O:D /B') DO SET V=%I

en un archivo por lotes, debe usar el prefijo doble en la variable de bucle:

FOR /F %%I IN ('DIR *.* /O:D /B') DO SET V=%%I
PabloG
fuente
2
He visto este enfoque antes, pero parece realmente hacky. También tiene el problema de que solo capturará la última línea de salida del comando en la variable.
John Meagher
3

Solo usa el resultado del FORcomando. Por ejemplo (dentro de un archivo por lotes):

for /F "delims=" %%I in ('dir /b /a-d /od FILESA*') do (echo %%I)

Puede usar el %%Icomo el valor que desee. Al igual que este: %%I.

¡Y de antemano %%Ino tiene espacios ni caracteres CR y puede usarse para comparaciones!

Raceableability
fuente
2

Si está buscando la solución proporcionada en ¿Cómo usar el resultado de un comando como argumento en bash?

entonces aquí está el código:

@echo off
if not "%1"=="" goto get_basename_pwd
for /f "delims=X" %%i in ('cd') do call %0 %%i
for /f "delims=X" %%i in ('dir /o:d /b') do echo %%i>>%filename%.txt
goto end

:get_basename_pwd
set filename=%~n1

:end
  • Esto se llamará a sí mismo con el resultado del comando CD, igual que pwd.
  • La extracción de cadenas en los parámetros devolverá el nombre de archivo / carpeta.
  • Obtenga el contenido de esta carpeta y añádalo al nombre del archivo.txt

[Créditos] : Gracias a todas las otras respuestas y algunas excavaciones en la página de comandos de Windows XP .

Gustavo Carreño
fuente
2
@echo off

ver | find "6.1." > nul
if %ERRORLEVEL% == 0 (
echo Win7
for /f "delims=" %%a in ('DIR "C:\Program Files\Microsoft Office\*Outlook.EXE" /B /P /S') do call set findoutlook=%%a
%findoutlook%
)

ver | find "5.1." > nul
if %ERRORLEVEL% == 0 (
echo WinXP
for /f "delims=" %%a in ('DIR "C:\Program Files\Microsoft Office\*Outlook.EXE" /B /P /S') do call set findoutlook=%%a
%findoutlook%
)
echo Outlook dir:  %findoutlook%
"%findoutlook%"
Caminata
fuente
Gracias por publicar una respuesta! Si bien un fragmento de código podría responder la pregunta, sigue siendo genial agregar información adicional, como explicar, etc.
j0k
2

Me gustaría agregar un comentario a las soluciones anteriores:

Todas estas sintaxis funcionan perfectamente bien SI SU COMANDO SE ENCUENTRA DENTRO DEL CAMINO o SI EL COMANDO ES UN CMDpath SIN ESPACIOS O CARACTERES ESPECIALES.

Pero si intenta usar un comando ejecutable ubicado en una carpeta cuya ruta contiene caracteres especiales, entonces necesitará encerrar su ruta de comando entre comillas dobles (") y luego la sintaxis FOR / F no funciona.

Ejemplos:

$ for /f "tokens=* USEBACKQ" %f in (
    `""F:\GLW7\Distrib\System\Shells and scripting\f2ko.de\folderbrowse.exe"" Hello '"F:\GLW7\Distrib\System\Shells and scripting"'`
) do echo %f
The filename, directory name, or volume label syntax is incorrect.

o

$ for /f "tokens=* USEBACKQ" %f in (
      `"F:\GLW7\Distrib\System\Shells and scripting\f2ko.de\folderbrowse.exe" "Hello World" "F:\GLW7\Distrib\System\Shells and scripting"`
) do echo %f
'F:\GLW7\Distrib\System\Shells' is not recognized as an internal or external command, operable program or batch file.

o

`$ for /f "tokens=* USEBACKQ" %f in (
     `""F:\GLW7\Distrib\System\Shells and scripting\f2ko.de\folderbrowse.exe"" "Hello World" "F:\GLW7\Distrib\System\Shells and scripting"`
) do echo %f
'"F:\GLW7\Distrib\System\Shells and scripting\f2ko.de\folderbrowse.exe"" "Hello' is not recognized as an internal or external command, operable program or batch file.

En ese caso, la única solución que encontré para usar un comando y almacenar su resultado en una variable es establecer (temporalmente) el directorio predeterminado en el comando en sí:

pushd "%~d0%~p0"
FOR /F "tokens=* USEBACKQ" %%F IN (
    `FOLDERBROWSE "Hello world!" "F:\GLW7\Distrib\System\Layouts (print,display...)"`
) DO (SET MyFolder=%%F)
popd
echo My selected folder: %MyFolder%

El resultado es entonces correcto:

My selected folder: F:\GLW7\Distrib\System\OS install, recovery, VM\
Press any key to continue . . .

Por supuesto, en el ejemplo anterior, supongo que mi script por lotes se encuentra en la misma carpeta que la de mi comando ejecutable para que pueda usar la sintaxis "% ~ d0% ~ p0". Si este no es su caso, entonces debe encontrar una manera de ubicar su ruta de comando y cambiar el directorio predeterminado a su ruta.

NB: Para aquellos que se preguntan, el comando de muestra utilizado aquí (para seleccionar una carpeta) es FOLDERBROWSE.EXE. Lo encontré en el sitio web f2ko.de ( http://f2ko.de/en/cmd.php ).

Si alguien tiene una mejor solución para ese tipo de comandos accesibles a través de una ruta compleja, me alegrará saberlo.

Gilles

Gilles Maisonneuve
fuente
Puede prefijar su comando con CALL. FOR / F no funciona si la ruta tiene espacios
jeb
@jeb: ¡MUCHAS GRACIAS! Ahora mi código < CALL "%SOURCEDIR%\FOLDERBROWSE" ... "quoted parameters"> funciona perfectamente.
Gilles Maisonneuve
1

Puede capturar toda la salida en una variable, pero las líneas estarán separadas por un carácter de su elección (# en el ejemplo a continuación) en lugar de un CR-LF real.

@echo off
setlocal EnableDelayedExpansion
for /f "delims=" %%i in ('dir /b') do (
    if "!DIR!"=="" (set DIR=%%i) else (set DIR=!DIR!#%%i)
)
echo directory contains:
echo %DIR%

Segunda versión, si necesita imprimir el contenido línea por línea. Esto aprovecha el hecho de que no habrá líneas de salida duplicadas de "dir / b", por lo que puede no funcionar en el caso general.

@echo off
setlocal EnableDelayedExpansion
set count=0
for /f "delims=" %%i in ('dir /b') do (
    if "!DIR!"=="" (set DIR=%%i) else (set DIR=!DIR!#%%i)
    set /a count = !count! + 1
)

echo directory contains:
echo %DIR%

for /l %%c in (1,1,%count%) do (
    for /f "delims=#" %%i in ("!DIR!") do (
        echo %%i
        set DIR=!DIR:%%i=!
    )
)
Adam Mitz
fuente
0
@echo off
setlocal EnableDelayedExpansion
FOR /F "tokens=1 delims= " %%i IN ('echo hola') DO (
    set TXT=%%i
)
echo 'TXT: %TXT%'

el resultado es 'TXT: hola'

ivan rc
fuente
0

Debe usar el forcomando, aquí hay un ejemplo:

@echo off
rem Commands go here
exit /b
:output
for /f "tokens=* useback" %%a in (`%~1`) do set "output=%%a"

y puede usar, call :output "Command goes here"entonces la salida estará en la %output%variable.

Nota: Si tiene una salida de comando que es multilínea, esta herramienta mostrará setla última línea de su comando multilínea.

Hayz
fuente
-1

Consulte esta http://technet.microsoft.com/en-us/library/bb490982.aspx que explica lo que puede hacer con la salida del comando.

Jack B Nimble
fuente
1
Ese artículo solo cubre la redirección y temas relacionados. No responde a la pregunta de cómo tomar la salida de un comando y ponerlo en una variable.
John Meagher
3
Usando su respuesta, se me ocurrió esto: git pull>0 set /P RESULTVAR=<0 editar: supongo, no sé cómo usar saltos de línea aquí ... cada comando está en su propia línea.
RibeiroBreno