¿Cómo llamo a una subrutina cuyo parámetro es una variable que contiene un signo (&)?
No hay ningún error, pero la llamada nunca parece ejecutarse.
ejemplo.bat
@echo off
setlocal enableDelayedExpansion
rem Doesn't work
set val=with^&ersand
call :Output !val!
rem Works fine
set val=without_ampersand
call :Output !val!
goto End
:Output
set "line=%1"
echo Called: !line!
goto :eof
End:
Producción:
Llamado: sin_ampersand
Editar:
El uso deExpansión retrasadano es requerido. Se acaba de utilizar en este ejemplo. Se prefiere una forma de hacerlo sin utilizar delayExpansion.
La pregunta se centra en "¿Cómo llamo?" en lugar de "¿Cómo configuro inicialmente la variable?". La variable puede provenir de la entrada del usuario o de un for /f
bucle (como es mi caso).
Respuesta1
Las reglas de escape por lotes son bastante desagradables, pero el comportamiento es totalmente predecible si las conoces.
La información que necesita para comprender el problema está disponible en¿Cómo analiza los scripts el intérprete de comandos de Windows (CMD.EXE)?en las fases 1, 2, 5 y 6 de la respuesta aceptada. Pero buena suerte para absorber esa información pronto :-)
Hay dos cuestiones de diseño fundamentales que conducen a su problema: - La fase 6 duplica todos los signos de intercalación, que luego reinicia la fase 2 (en realidad, las fases 1, 1,5 y 2). - Pero la fase 2 requiere &
escaparse como ^&
. Tenga en cuenta que debe ser simple ^
, ¡no duplicado!
La única forma de hacer que su enfoque funcione es introducir el ^
después de que se haya producido la duplicación del cursor de la fase 6.
@echo off
setlocal enableDelayedExpansion
set "ESC=^"
rem Calling with delayed expansion value
set "val=with%%ESC%%&ersand"
call :Output !val!
rem Calling with a string literal
call :Output with%%ESC%%^&ersand
exit /b
:Output
set "line=%1"
echo Called: !line!
goto :eof
ESC está definido para mantener ^
.
La primera ronda de la fase 1 se expande %%ESC%%
a %ESC%
la segunda ronda de la fase 1 (iniciada por la fase 6) se expande %ESC%
a^
Todo esto es totalmente impráctico, especialmente si no sabes cuál será el contenido.
La única estrategia sensata para pasar de manera confiable cualquier valor a una rutina CALLed es pasar por referencia. Pase el nombre de una variable que contenga el valor de la cadena y expanda ese valor dentro de su subrutina usando la expansión retardada.
@echo off
setlocal enableDelayedExpansion
set "val=with&ersand"
call :Output val
exit /b
:Output
set "line=!%~1!"
echo Called: !line!
exit /b
Respuesta2
Si agrega ^
comillas adicionales, funcionará:
@echo off
setlocal enableDelayedExpansion
rem Doesn't work
set "val=with^^&ersand"
call :Output !val!
rem Works fine
set "val=without_ampersand"
call :Output !val!
goto End
:Output
set "line=%1"
echo Called: !line!
goto :eof
SET LOCAL EnableDelayedExpansion
requiere 2 caracteres de escape:
ECHO 123 ^^& 456^^!
saldrá 123 & 456!
.
¡También debes usar SET
siempre entre comillas!
Respuesta3
Las otras respuestas no funcionaron para mí, ya que & todavía se leía como un separador de comandos, pero se me ocurrió esto (el último bucle for es para demostración y debe modificarse o eliminarse según sea necesario):
@echo off
setlocal enableDelayedExpansion
set "file=pathwith&orwithoutampersandand!exclamation"
call :execute
exit /b
:execute
for %%A in ("%file:!=" "%") do (
if [!file_conc!]==[] (
set "file_conc=%%A"
) else (
set "file_conc=!file_conc!^^^!%%A"
)
)
set "file=!file_conc:"=!"
set "file_conc="
for %%A in ("!file:&=" "!") do (
if [!file_conc!]==[] (
set "file_conc=%%A"
) else (
set "file_conc=!file_conc!^^^&%%A"
)
)
set "file=!file_conc:"=!"
set "file_conc="
for /f "tokens=*" %%A in ('cmd /c ""!file!" "%error_log%" %ldt%" 1^>nul') do stuff
exit /b
Respuesta4
Los caracteres venenosos como <>|&
están seguros entre comillas. Tienes que usar comillas cada vez, tienes que manejarlas (definirlas o usarlas) (o escapar de ellas). Eso comienza con el set
comando (definición de variable), continúa con el parámetro to call
(uso de variable) y continúa (pero de lejos no termina) con el echo
comando (uso de variable).
@echo off
setlocal
set "val=with&ersand"
call :Output "%val%"
exit /b
:Output
set "line=%~1"
set line
echo problem without quotes: %line%
echo ok with quotes: "%line%"
echo ok with escaping: %line:&=^&%
for /f "delims=" %%a in ("%line%") do echo ok with for: %%a
Producción:
line=with&ersand
problem without quotes: with
Der Befehl "ampersand" ist entweder falsch geschrieben oder
konnte nicht gefunden werden.
ok with quotes: "with&ersand"
ok with escaping: with&ersand
ok with for: with&ersand