Como chamo uma sub-rotina com um parâmetro sendo uma variável contendo um e comercial (&)?
Não há erro, mas a chamada parece nunca ser executada.
exemplo.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:
Saída:
Chamado: sem_ampersand
Editar:
O uso deExpansão atrasadanão é necessário. Foi usado apenas neste exemplo. É preferível uma maneira de fazer isso sem usar atrasadoExpansion.
A questão está focada em "Como faço para chamar" em vez de "Como faço para definir inicialmente a variável". A variável pode vir da entrada do usuário ou de um for /f
loop (como é o meu caso).
Responder1
As regras de escape em lote são bastante desagradáveis, mas o comportamento é totalmente previsível se você conhecer as regras.
As informações que você precisa para entender o problema estão disponíveis emComo o Windows Command Interpreter (CMD.EXE) analisa scripts?nas fases 1, 2, 5 e 6 da resposta aceita. Mas boa sorte para absorver essa informação em breve :-)
Existem dois problemas fundamentais de design que levam ao seu problema: - A fase 6 duplica todos os sinais circunflexos, que então reinicia a fase 2 (na verdade, fases 1, 1.5 e 2). - Mas a fase 2 exige &
ser escapada como ^&
. Observe que deve ser single ^
, não duplicado!
A única maneira de fazer sua abordagem funcionar é introduzi-la ^
após a duplicação do sinal de intercalação da fase 6 ter ocorrido.
@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 é definido para segurar ^
.
A primeira rodada da fase 1 se expande %%ESC%%
para %ESC%
a segunda rodada da fase 1 (iniciada pela fase 6) se expande %ESC%
para^
Tudo isso é totalmente impraticável, principalmente se você não sabe qual será o conteúdo.
A única estratégia sensata para passar qualquer valor de forma confiável para uma rotina CALL é passar por referência. Passe o nome de uma variável que contém o valor da string e expanda esse valor dentro de sua sub-rotina usando expansão atrasada.
@echo off
setlocal enableDelayedExpansion
set "val=with&ersand"
call :Output val
exit /b
:Output
set "line=!%~1!"
echo Called: !line!
exit /b
Responder2
Se você adicionar um adicional ^
e aspas, 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
requer 2 caracteres de escape:
ECHO 123 ^^& 456^^!
irá produzir 123 & 456!
.
Você também deve usar SET
sempre entre aspas!
Responder3
As outras respostas não funcionaram para mim, pois o & ainda estava sendo lido como um separador de comandos, mas eu descobri isso (o último loop for é para demonstração e deve ser modificado ou removido conforme necessário):
@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
Responder4
Caracteres venenosos como <>|&
estão seguros entre aspas. Você tem que usar aspas todas as vezes, tem que lidar (definir ou usar) elas (ou escapar delas). Isso começa com o set
comando (definição de variável), continua com o parâmetro para call
(uso de variável) e continua (mas de longe não termina) com o echo
comando (uso de variável).
@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
Saída:
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