Llame a una subrutina donde el parámetro contiene el signo comercial en un archivo por lotes

Llame a una subrutina donde el parámetro contiene el signo comercial en un archivo por lotes

¿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^&ampersand
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 /fbucle (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%%&ampersand"
call :Output !val!

rem Calling with a string literal
call :Output with%%ESC%%^&ampersand

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&ampersand"
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^^&ampersand"
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 EnableDelayedExpansionrequiere 2 caracteres de escape:

ECHO 123 ^^& 456^^! saldrá 123 & 456!.

¡También debes usar SETsiempre 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 setcomando (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 echocomando (uso de variable).

@echo off
setlocal 
set "val=with&ampersand"
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&ampersand
problem without quotes: with
Der Befehl "ampersand" ist entweder falsch geschrieben oder
konnte nicht gefunden werden.
ok with quotes: "with&ampersand"
ok with escaping: with&ampersand
ok with for: with&ampersand

información relacionada