アンパサンド (&) を含む変数をパラメータとしてサブルーチンを呼び出すにはどうすればよいでしょうか?
エラーはありませんが、呼び出しが実行されないようです。
例.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:
出力:
呼び出し元: without_ampersand
編集:
の使用法遅延拡張必須ではありません。この例では単に使用しました。delayedExpansion を使用せずに実行する方法をお勧めします。
質問は、「変数を最初に設定するにはどうすればよいか」ではなく、「どのように呼び出すか」に焦点を当てています。変数は、ユーザー入力またはループから取得される場合がありますfor /f
(私の場合がそうです)。
答え1
バッチ エスケープ ルールは非常に厄介ですが、ルールを知っていれば動作は完全に予測可能です。
問題を理解するために必要な情報は、Windows コマンド インタープリター (CMD.EXE) はスクリプトをどのように解析しますか?受け入れられた回答のフェーズ 1、2、5、6 で。しかし、すぐにその情報を吸収できるといいですね :-)
問題の原因となっている基本的な設計上の問題が 2 つあります。 - フェーズ 6 ではすべてのキャレットが 2 倍になり、フェーズ 2 (実際にはフェーズ 1、1.5、および 2) が再開されます。 - ただし、フェーズ 2 では&
としてエスケープする必要があります^&
。 2 倍ではなく、単一の である必要があることに注意してください^
。
アプローチを機能させる唯一の方法は、^
フェーズ 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は を保持するように定義されます^
。
フェーズ1の最初のラウンドは%%ESC%%
に拡大し%ESC%
、フェーズ1の2番目のラウンド(フェーズ6によって開始)%ESC%
は に拡大します。^
特にコンテンツの内容がわからない場合、これはまったく非現実的です。
CALL されたルーチンに値を確実に渡す唯一の賢明な戦略は、参照渡しです。文字列値を含む変数の名前を渡し、遅延展開を使用してサブルーチン内でその値を展開します。
@echo off
setlocal enableDelayedExpansion
set "val=with&ersand"
call :Output val
exit /b
:Output
set "line=!%~1!"
echo Called: !line!
exit /b
答え2
引用符を追加すれば^
機能します:
@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
2 つのエスケープ文字が必要です:
ECHO 123 ^^& 456^^!
を出力します123 & 456!
。
SET
常に引用符を付けて使用する必要があります。
答え3
他の回答は & が依然としてコマンド区切り文字として読み取られていたため、私にはうまくいきませんでしたが、私は次の回答を思いつきました (最後の for ループはデモンストレーション用であり、必要に応じて変更または削除する必要があります)。
@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
答え4
のような有害な文字は<>|&
引用符内では安全です。毎回引用符を使用し、それらを処理 (定義または使用) (またはエスケープ) する必要があります。これはコマンドset
(変数定義) で始まり、パラメーターcall
(変数使用) に続き、コマンド (変数使用) に続きます (ただし、これで終わりではありません) echo
。
@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
出力:
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