batch-file - informatica - echo off bat
Cómo evitar que cmd.exe interprete caracteres especiales del shell como<> ^ (2)
Esto ayuda:
EscapPipes.Cmd :
@echo off
:Start
If [%1]==[] goto :eof
@Echo %1
shift
goto :Start
Cuando comenzó así:
EscapPipes.Cmd Andy Pandy "Pudding | > < and pie"
da
Andy
Pandy
"Pudding | > < and pie"
Tan pronto como elimine las comillas, los símbolos de las tuberías se activarán.
Tengo un script de Windows CMD que acepta una serie de parámetros y ejecuta un EXE, pasando primero algunos argumentos codificados y luego todos los parámetros del usuario. La secuencia de comandos CMD se ve así:
launcher.exe paramX paramY %*
El usuario ejecutaría la secuencia de comandos CMD desde el shell de Windows de la siguiente manera:
launcher.cmd param1 param2 param3 [...]
El problema que tengo es que si los parámetros de la secuencia de comandos CMD contienen caracteres especiales de shell como <
>
y ^
, el usuario se ve obligado a escapar de estos precediendo a cada uno con 3 caracteres de escape caret ^
shell.
Dos ejemplos
1) Para pasar el argumento ten>one
al EXE, el usuario debe iniciar el CMD de la siguiente manera:
launcher.cmd ten^^^>one
La razón de esto es que los caracteres especiales del shell ^
y >
son interpretados por el shell del comando en dos niveles, primero en la línea de comando y segundo dentro del script CMD. Por lo tanto, el shell que se escapa con el carácter de escape caret ^
shell debe aplicarse dos veces. El problema es que esto no es obvio para el usuario y se ve feo.
Para este ejemplo, una solución más agradable es rodear el argumento con comillas dobles. Sin embargo, esto se descompone para ejemplos más complejos que incluyen una comilla doble literal en el argumento.
2) Para pasar el argumento "^
al EXE, el usuario debe iniciar el CMD de la siguiente manera:
launcher.cmd "/"^^^^"
En mi caso, deseo admitir argumentos que contengan cualquier secuencia de caracteres ASCII bajos, excluyendo caracteres de control, es decir, puntos de código 0x20 a 0x7E. Entiendo que habrá ejemplos en los que el usuario tendrá que escapar de ciertos caracteres especiales de shell con un símbolo de intercalación. Sin embargo, no quiero que el usuario tenga que usar 3 caretas cada vez en estos casos simplemente porque llaman a un script CMD en lugar de un EXE.
Puedo resolver este problema reemplazando el script CMD con un EXE que hace lo mismo. Sin embargo, ¿hay alguna forma de alterar el script de CMD para que pase sus parámetros a través del EXE sin interpretar los caracteres especiales del shell?
Una forma es trabajar con la expansión retrasada dentro del lote, porque entonces los caracteres especiales pierden significados "especiales".
El único problema es obtener los parámetros en una variable.
Algo como esto podría ayudar
@echo off
setlocal DisableDelayedExpansion
rem ** At this point the delayedExpansion should be disabled
rem ** otherwise an exclamation mark in %1 can remove carets
set "param1=%~1"
setlocal EnableDelayedExpansion
rem ** Now you can use the param1, independent of the content, even with carets or quotes
rem ** but be careful with call''s, because they start a second round of expansion
echo !param1!
set "tmp=!param1:~1,4!"
Ahora los parámetros pueden estar rodeados por comillas, por lo que ya no son necesarios los intercalados. Ejemplo launcher.bat "abc> def & geh% ijk | lmn ^ opq!"
El único carácter especial problemático restante parece ser la comilla.
[Editar / Mejorar] Creo otra forma de recuperar un parámetro, supongo que puede aceptar cualquier cadena y también su segundo ejemplo.
Incluso cadenas realmente duras como
lanzador "^
lanzador diez ^> uno
lanzador "&" ^ &
@echo off
setlocal DisableDelayedExpansion
set "prompt=X"
for %%a in (1 ) do (
@echo on
for %%b in (4) do (
rem #%1#
)
) > XY.txt
@echo off
for /F "delims=" %%a in (xy.txt) DO (
set "param=%%a"
)
setlocal EnableDelayedExpansion
set param=!param:~7,-4!
echo param=''!param!''
¿Cómo funciona?
La única forma que he encontrado para expandir% 1 sin expandir los caracteres especiales como "o ^ está en una declaración REM (para REM no es completamente cierto, pero esa es otra historia) Bien, el único problema es que un REM es un observación y no tiene efecto :-)
Pero si usa echo en también, las líneas rem se repiten antes de que se ejecuten (ejecutar para rem es una palabra bonita).
El siguiente problema es que se muestra y no se puede redirigir esta salida de depuración con el normal> depuración.txt. Esto también es cierto si usa un for-loop.
Ok, puedes redirigir el eco en la salida con una llamada como
echo on
call :myFunc > debug.txt
Pero si llama a una función ya no puede acceder al% 1 del archivo por lotes.
Pero con un doble for-loop, es posible activar la redirección para la salida de depuración y aún es posible acceder a% 1.
Cambio el mensaje a "X", así que sé que siempre tiene solo un carácter.
Lo único que queda es explicar por qué añado un # a% 1.
Eso es porque, algunos personajes especiales son reconocidos en algunas situaciones incluso en una línea REM, obviamente ;-)
rem This is a remark^
rem This_is_a_multiline^
rem "This is also a multiline"^
Entonces el # suprime una posible situación de líneas múltiples.