バッチファイルでパラメータにアンパサンドが含まれるサブルーチンを呼び出す

バッチファイルでパラメータにアンパサンドが含まれるサブルーチンを呼び出す

アンパサンド (&) を含む変数をパラメータとしてサブルーチンを呼び出すにはどうすればよいでしょうか?

エラーはありませんが、呼び出しが実行されないようです。

例.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:

出力:

呼び出し元: 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%%&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は を保持するように定義されます^
フェーズ1の最初のラウンドは%%ESC%%に拡大し%ESC%
、フェーズ1の2番目のラウンド(フェーズ6によって開始)%ESC%は に拡大します。^

特にコンテンツの内容がわからない場合、これはまったく非現実的です。

CALL されたルーチンに値を確実に渡す唯一の賢明な戦略は、参照渡しです。文字列値を含む変数の名前を渡し、遅延展開を使用してサブルーチン内でその値を展開します。

@echo off
setlocal enableDelayedExpansion
set "val=with&ampersand"
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^^&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 EnableDelayedExpansion2 つのエスケープ文字が必要です:

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&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

出力:

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

関連情報