Solución:
Soluciones alternativas:
-
Llame a su archivo por lotes a través de
cmd /c call
, en cuyo caso el|| exit /b
solución sin un código de salida explícito funciona como se esperaba.-
cmd /c call .test.bat
-
Utilizando
cmd /c call
rutinariamente para llamar archivos por lotes desde el exteriorcmd.exe
es aconsejable, como incluso archivos por lotes sinexit
De lo contrario, las llamadas pueden comportarse de forma inesperada; consulte esta respuesta.
-
-
Alternativamente, pero solo si su archivo por lotes nunca necesita ser llamado de otro archivo por lotes a qué control se debe devolver y si nunca necesita ser parte de un
cmd /c
multi-comando línea de comando donde no es el último mando[1] – puedes usar|| exit
en lugar de|| exit /b
– esto sale de la ejecucióncmd.exe
proceso como un todo, instantáneamente, pero el código de salida (nivel de error) es luego informado de manera confiable (al menos en el contexto de un<command> || exit
declaración) también con directo invocación desde fueracmd.exe
, tal como& .test.bat
(o, en este simple caso, simplemente.test.bat
) de PowerShell.
Mientras combina setlocal EnableDelayedExpansion
con exit /b !ERRORLEVEL!
también funciona, debido al uso de un explícito código de salida: obviamente es más engorroso y puede tener efectos secundarios, en particular, eliminar silenciosamente !
caracteres de comandos como echo hi!
(aunque es posible minimizar ese problema colocando el setlocal EnableDelayedExpansion
llamar a la línea justo antes de un exit /b
llamada, que requeriría la duplicación si hay varios puntos de salida).
cmd.exe
El comportamiento es desafortunado en este caso, pero no se puede evitar.
Al llamar a un archivo por lotes desde fuera cmd.exe
:
-
exit /b
– sin un argumento de código de salida (nivel de error): solo establece elcmd.exe
procesar código de salida como se esperaba, es decir, al código de salida del comando ejecutado más recientemente en el archivo por lotes – si llama al archivo por lotes concall
, es decir, comocmd /c call <batch-file>
-
Sin
call
, un argumento sin argumentosexit /b
llamar desde un archivo por lotes es reflejado en el%ERRORLEVEL%
variable intra-cmd.exe
-sesión, pero eso no se traduce encmd.exe
‘s proceso código de salida, que por defecto es0
.[1] -
Con
call
, sin argumentosexit /b
lo hace correctamente configuradocmd.exe
código de salida, incluso en un<command> || exit /b
declaración, en cuyo caso<command>
El código de salida se retransmite, según lo previsto.
-
-
exit /b <code>
, es decir, pasar un código de salida<code>
explícitamente, siempre obras[2], es decircall
es entonces no necesario. -
Esta distinción es una Inconsistencia oscura que podría justificarse como un error.; La útil respuesta de Jeb tiene un código de muestra que demuestra el comportamiento.
[1] Con cmd /c
, puedes pasar múltiple declaraciones para la ejecución, y es el último declaración que determina el cmd.exe
código de salida del proceso. P.ej, cmd /c "ver & dir nosuch"
informa el código de salida 1
, porque el elemento del sistema de archivos inexistente nosuch
causado dir
para establecer el nivel de error en 1
, independientemente de si el comando anterior (ver
) tuvo éxito. La inconsistencia es que, para un archivo por lotes llamado test.bat
que sale con exit /b
sin un argumento de código de salida explícito, cmd /c test.bat
siempre informa 0
, mientras que cmd /c call test.bat
informa correctamente el código de salida del última declaración ejecutada antes de que saliera el archivo por lotes.
[2] El código de salida puede especificarse literalmente o mediante una variable, pero el problema es que, debido a cmd.exe
expansión variable inicial – <command> || exit /b %ERRORLEVEL%
lo hace no funciona según lo previsto, porque %ERRORLEVEL%
en ese punto se expande al nivel de error previo a esta declaración, no a la establecida por <command>
; esta es la razón por demorado expansión, a través de haber corrido setlocal enabledelayedexpansion
o haber invocado el cmd.exe
con el /V
opción, es necesaria en este caso: <command> || exit /b !ERRORLEVEL!
Hay una diferencia entre exit /b
y exit /b <code>
.
Como dice mklement0, la diferencia se hace visible al llamar a un archivo por lotes con o sin CALL
En mis pruebas, usé (call)
para forzar el nivel de error a 1.
test1.bat
@echo off
(call)
exit /b
test2.bat
@echo off
(call)
exit /b %errorlevel%
Probando con prueba-todo.bat:
cmd /c "test1.bat" & call echo Test1 %%errorlevel%%
cmd /c "call test1.bat" & call echo call Test1 %%errorlevel%%
cmd /c "test2.bat" & call echo Test2 %%errorlevel%%
cmd /c "call test2.bat" & call echo call Test2 %%errorlevel%%
Producción:
Test1 0 call Test1 1 Test2 1 call Test2 1
Para obtener un nivel de error siempre confiable, debe usar la forma implícita de exit /b <code>
.
En caso de utilizar la construcción <command> || exit /b !errorlevel!
la expansión retardada es necesaria o la forma
<command> || call exit /b %%errorlevel%%
Otra solución
<command> || call :exit
...
:exit
(
(goto) 2>nul
exit /b
)
Esto usa el manejo de excepciones por lotes
¿Windows admite el manejo de excepciones por lotes?
Veamos los tres posibles escenarios:
cmd /c "exit 99" || exit /b
devoluciones 0
porque cmd /c "exit 99"
ejecutado correctamente
cmd /c "exit 99" || exit /b !errorlevel!
devoluciones 99
porque cmd /c "exit 99"
colocar errorlevel
a 99 y estamos devolviendo el nivel de error que resultados de ejecutar cmd /c "exit 99"
cmd /c "exit 99" || exit /b %errorlevel%
devoluciones ?
– nivel de error como estaba cuando el cmd /c "exit 99"
La línea se analizó ya que en ese momento se evaluó ‘% errorlevel%.
Si delayedexpansion
era no , entonces la única diferencia es que el !errorlevel!
El escenario intenta asignar una cadena al nivel de error, que probablemente no funcionará muy bien.
En cuanto a Powershell, es un caso de esquina en una carretera menos transitada. Un escenario que no se probó a fondo como los diseñadores esperaban ejecutar .exe
s, etc. utilizando esta función. Sin duda, incluso si se informa, no se solucionaría, ya que hay una solución alternativa, incluso si no está bien expuesto.
Este es, creo, el escenario de falla a falla: una instalación que se supone que funciona porque rara vez se cumplen las condiciones adecuadas para hacerla fallar.
Del mismo modo,
echo %%%%b
es ambiguo. ¿Significa echo
el literal %%b
o para echo
%
prefijado al contenido de la metavariable b
? (Respuesta: este último). No se encuentra exactamente todos los días. ¿Qué posibilidades hay de que se resuelva la ambigüedad, alguna vez? Ni siquiera podemos conseguir /u
implementado en el date
comando para que entregue la fecha en un formato universal que resolvería la gran mayoría de las preguntas orientadas a la fecha publicadas en el batch
etiqueta. En cuanto a la posibilidad de un interruptor para permitir date
para entregar días-desde-alguna-época-fecha – ni siquiera me he molestado en sugerirlo desde entonces, a pesar de invitar sugerencias para cmd
modificaciones, absolutamente nada Se ha realizado sobre las instalaciones ofrecidas, solo cambios en la interfaz de usuario. Jugando con todas las cosas brillantes mientras los viejos fieles languidecen en el fondo de un archivador cerrado y metido en un baño en desuso con un letrero en la puerta que dice “Cuidado con el leopardo”.