This question comenzó la discusión sobre este punto, y algunas pruebas se realizaron para determinar por qué. Así, después de una cierta depuración en el interior cmd.exe
... (esto es para un 32 bits de Windows XP cmd.exe pero a medida que el comportamiento es consistente en las versiones más recientes del sistema, probablemente se utiliza la misma o similar código)
Dentro de Jeb la respuesta se dice
It's a problem with the quotes and %~0.
cmd.exe handles %~0 in a special way
y aquí Jeb es el correcto.
Dentro del contexto actual del archivo por lotes en ejecución hay una referencia al archivo por lotes actual, una "variable" que contiene la ruta completa y el nombre de archivo del archivo por lotes en ejecución.
Cuando se accede a una variable, su valor se recupera de una lista de variables disponibles pero si la variable solicitada es %0
, y se ha solicitado algún modificador (~
se utiliza), entonces los datos de la referencia del lote se ejecuta "variable" es usado.
Pero el uso de ~
tiene otro efecto en las variables. Si el valor es cotizado, las cotizaciones se eliminan. Y aquí hay un error en el código. Se codifica algo así como (ensamblador aquí simplificado para pseudocódigo)
value = varList[varName]
if (value && value[0] == quote){
value = unquote(value)
} else if (varName == '0') {
value = batchFullName
}
Y sí, esto significa que cuando se cita el archivo por lotes, la primera parte de la if
se ejecuta y la referencia completa al archivo por lotes no es utilizado, en cambio, el valor recuperado es la cadena utilizada para hacer referencia al archivo por lotes cuando lo llama.
¿Qué sucede entonces? Si cuando se llamó al archivo por lotes se utilizó la ruta completa, entonces no habrá problema. Pero si la ruta completa no se utiliza en la llamada, se debe recuperar cualquier elemento de la ruta que no esté presente en la llamada por lotes. Esta recuperación asume caminos relativos.
un simple archivo por lotes (test.cmd
)
@echo off
echo %~f0
Cuando se llama usando test
(sin extensión, sin comillas), obtenemos c:\somewhere\test.cmd
Cuando se llama usando "test"
(sin extensión, comillas), obtenemos c:\somewhere\test
En el primer caso, sin comillas, se utiliza el valor interno correcto. En el segundo caso, cuando se cita la llamada, la cadena utilizada para llamar al archivo de proceso por lotes ("test"
) no se cita ni se utiliza. Como estamos solicitando una ruta completa, se considera una referencia relativa a algo llamado test
.
Este es el motivo. ¿Cómo resolver?
Desde el código C#
No use comillas: cmd /c batchfile.cmd
Si se necesitan comillas, utilice la ruta completa en la llamada al archivo por lotes. De esa manera, %0
contiene toda la información necesaria.
Desde el archivo por lotes
archivo por lotes puede ser invocada en cualquier forma desde cualquier lugar. La única forma confiable de recuperar la información del archivo por lotes actual es usar una subrutina. Si se utiliza cualquier modificador (~
), el %0
usará la "variable" interna para obtener los datos.
@echo off
setlocal enableextensions disabledelayedexpansion
call :getCurrentBatch batch
echo %batch%
exit /b
:getCurrentBatch variableName
set "%~1=%~f0"
goto :eof
Esto hará eco a la consola la ruta completa al archivo por lotes actual independtly de cómo se llama el archivo, con o sin comillas.
nota: ¿Por qué funciona? ¿Por qué la referencia %~f0
dentro de una subrutina devuelve un valor diferente? Los datos a los que se accede desde el interior de la subrutina no son los mismos. Cuando se ejecuta call
, se crea un nuevo contexto de archivo por lotes en la memoria y la "variable" interna se utiliza para inicializar este contexto.
Puede replicar el comportamiento cuando ejecuta el lote desde 'cmd' mediante' "mybatfile.cmd" '(yes, * with * quotes). Esa es exactamente la invocación que obtienes al ejecutar a través de 'Process.Start' como también puedes verificar mediante' echo'ing '% 0'. – Joey
Gracias Joey, tus sugerencias fueron el truco. Funciona para mi ahora. –
Bueno, fue solo una observación; Todavía no puedo explicar el comportamiento :-) – Joey