¿Hay alguna forma en un script por lotes de mantener la consola abierta solo si se invoca desde el Administrador de Windows?
-
21-08-2019 - |
Pregunta
Tengo un script por lotes de DOS que invoca una aplicación Java que interactúa con el usuario a través de la interfaz de usuario de la consola.Por el bien del argumento, llamémoslo runapp.bat
y su contenido sea
java com.example.myApp
Si el script por lotes se invoca en una consola, todo funciona bien.Sin embargo, si el script se invoca desde el Administrador de ventanas, la consola recién abierta se cierra tan pronto como la aplicación termina de ejecutarse.Lo que quiero es que la consola quede abierta en todos los casos.
Conozco los siguientes trucos:
Agrega un
pause
comando al final del script.Esto es un poco feo por si acaso.runapp.bat
se invoca desde la línea de comando.crear un nuevo shell usando
cmd /K java com.example.myApp
Esta es la mejor solución que encontré hasta ahora, pero deja un entorno de shell adicional cuando se invoca desde la línea de comando, por lo que llamarexit
en realidad no cierra el caparazón.
¿Existe una mejor manera?
Solución
Vea esta pregunta: La detección de cómo se ejecuta un archivo por lotes
Este script no se detendrá si se ejecuta desde la consola de comandos, pero si hizo doble clic en el Explorador:
@echo off
setlocal enableextensions
set SCRIPT=%0
set DQUOTE="
:: Detect how script was launched
@echo %SCRIPT:~0,1% | findstr /l %DQUOTE% > NUL
if %ERRORLEVEL% EQU 0 set PAUSE_ON_CLOSE=1
:: Run your app
java com.example.myApp
:EXIT
if defined PAUSE_ON_CLOSE pause
Otros consejos
Yo prefiero usar %cmdcmdline%
tal como fue anunciado en el comentario a la respuesta de Patrick a la otra pregunta (que no he encontrado aunque mirado). De esa manera, incluso si alguien decide usar comillas para llamar a la secuencia de comandos por lotes, no va a disparar el falso positivo.
Mi solución final:
@echo off
java com.example.myApp %1 %2
REM "%SystemRoot%\system32.cmd.exe" when from console
REM cmd /c ""[d:\path\script.bat]" " when from windows explorer
@echo %cmdcmdline% | findstr /l "\"\"" >NUL
if %ERRORLEVEL% EQU 0 pause
cmd /K java com.example.myApp & pause & exit
hará el trabajo. El y ejecutará el comando, uno tras otro. Si utiliza && se puede romper si uno falla.
Incluir esta línea en un archivo por lotes y haga doble clic en el archivo por lotes en el explorador:
cmd / k "secuencias de comandos dentro de estas citas separadas por &&"
Por ejemplo
cmd / k "cd ../ .. && && cd dir some_directory"
El complemento completo de opciones para cmd se puede encontrar aquí
Con frecuencia uso shells y subshells alternativos (principalmente TCC/LE de jpsoft.com).Descubrí que este código funciona para un caso más amplio y general (y no requiere FINDSTR):
@echo off & setlocal
if "%CMDEXTVERSION%"=="" ( echo REQUIRES command extensions & exit /b 1 ) &:: REQUIRES command extensions for %cmdcmdline% and %~$PATH:1 syntax
call :_is_similar_command _FROM_CONSOLE "%COMSPEC%" %cmdcmdline%
if "%_PAUSE_NEEDED%"=="0" ( goto :_START )
if "%_PAUSE_NEEDED%"=="1" ( goto :_START )
set _PAUSE_NEEDED=0
if %_FROM_CONSOLE% equ 0 ( set _PAUSE_NEEDED=1 )
goto :_START
::
:_is_similar_command VARNAME FILENAME1 FILENAME2
:: NOTE: not _is_SAME_command; that would entail parsing PATHEXT and concatenating each EXT for any argument with a NULL extension
setlocal
set _RETVAL=0
:: more than 3 ARGS implies %cmdcmdline% has multiple parts (therefore, NOT direct console execution)
if NOT [%4]==[] ( goto :_is_similar_command_RETURN )
:: deal with NULL extensions (if both NULL, leave alone; otherwise, use the non-NULL extension for both)
set _EXT_2=%~x2
set _EXT_3=%~x3
if NOT "%_EXT_2%"=="%_EXT_3%" if "%_EXT_2%"=="" (
call :_is_similar_command _RETVAL "%~2%_EXT_3%" "%~3"
goto :_is_similar_command_RETURN
)
if NOT "%_EXT_2%"=="%_EXT_3%" if "%_EXT_3%"=="" (
call :_is_similar_command _RETVAL "%~2" "%~3%_EXT_2%"
goto :_is_similar_command_RETURN
)
::if /i "%~f2"=="%~f3" ( set _RETVAL=1 ) &:: FAILS for shells executed with non-fully qualified paths (eg, subshells called with 'cmd.exe' or 'tcc')
if /i "%~$PATH:2"=="%~$PATH:3" ( set _RETVAL=1 )
:_is_similar_command_RETURN
endlocal & set "%~1=%_RETVAL%"
goto :EOF
::
:_START
if %_FROM_CONSOLE% EQU 1 (
echo EXEC directly from command line
) else (
echo EXEC indirectly [from explorer, dopus, perl system call, cmd /c COMMAND, subshell with switches/ARGS, ...]
)
if %_PAUSE_NEEDED% EQU 1 ( pause )
Inicialmente había usado if /i "%~f2"=="%~f3"
en el _is_similar_command
subrutina.el cambio a if /i "%~$PATH:2"=="%~$PATH:3"
y la verificación de código adicional para extensiones NULL permite que el código funcione para shells/subshells abiertos con rutas no completamente calificadas (por ejemplo, subshells llamados solo con 'cmd.exe' o 'tcc').
Para argumentos sin extensiones, este código no analiza ni utiliza las extensiones de %PATHEXT%.Básicamente, ignora la jerarquía de extensiones que utiliza CMD.exe cuando busca un comando sin extensión (primero intenta FOO.com, luego FOO.exe, luego FOO.bat, etc.).Entonces, _is_similar_command
comprueba la similitud, no la equivalencia, entre los dos argumentos como comandos de shell.Esto podría ser una fuente de confusión/error, pero, con toda probabilidad, nunca surgirá como un problema en la práctica para esta aplicación.
Editar: El código inicial era una versión antigua.El código ahora está actualizado a la versión más reciente que tiene:(1) un intercambiado %COMSPEC%
y %cmdcmdline%
en la llamada inicial, (2) agregó un cheque por múltiples %cmdcmdline%
argumentos, (3) los mensajes repetidos son más específicos sobre lo que se detecta, y (4) una nueva variable %_PAUSE_NEEDED%
fue añadido.
se debe notar que %_FROM_CONSOLE%
se establece en función específicamente de si el archivo por lotes se ejecutó directamente desde la línea de comandos de la consola o indirectamente a través del explorador o algún otro medio.Estos "otros medios" pueden incluir una llamada al sistema Perl() o ejecutando un comando como cmd /c COMMAND
.
La variable %_PAUSE_NEEDED%
Se agregó para que los procesos (como Perl) que ejecutan el archivo por lotes indirectamente puedan evitar las pausas dentro del archivo por lotes.Esto sería importante en los casos en los que la salida no se canaliza a la consola visible (p. ej., perl -e "$o = qx{COMMAND}"
).Si se produce una pausa en tal caso, aparecerá el mensaje "Presione cualquier tecla para continuar"..." El mensaje de pausa nunca se mostrará al usuario y el proceso se bloqueará esperando una entrada no solicitada del usuario.En los casos en los que la interacción del usuario no sea posible o no esté permitida, el %_PAUSE_NEEDED%
La variable se puede preestablecer en "0" o "1" (falso o verdadero respectivamente). %_FROM_CONSOLE%
todavía está configurado correctamente por el código, pero el valor de %_PAUSE_NEEDED%
no se establece posteriormente basándose en %_FROM_CONSOLE%
.Acaba de pasar.
Y también tenga en cuenta que el código detectará incorrectamente la ejecución como indirecta (%_FROM_CONSOLE%
=0) dentro de un subshell si ese subshell se abre con un comando que contiene modificadores/opciones (por ejemplo, cmd /x
).Generalmente esto no es un gran problema ya que los subshells generalmente se abren sin interruptores adicionales y %_PAUSE_NEEDED%
se puede establecer en 0, cuando sea necesario.
Advertencia codor.
@echo% CMDCMDLINE% | buscar / i "/ c"> nul && pausa