バッチ スクリプトでは遅延拡張はどのように機能しますか?

バッチ スクリプトでは遅延拡張はどのように機能しますか?

バッチ環境で実行される次の単純な一連のコマンドを見てみましょう。

>set ar[0]=orange
>set ar[1]=apple
>set ar[2]=banana
>for %i in (0,1,2) do echo !ar[%i]!

出力は次のようになります。

>echo !ar[0]!
!ar[0]!
>!ar[1]!
!ar[1]!
>!ar[2]!
!ar[2]!

したがって、明らかにその値まで拡張されません。どうすれば拡張できるでしょうか?

答え1

出典:
フェーズ5Windows コマンド インタープリター (CMD.EXE) はスクリプトをどのように解析しますか?

遅延拡張:遅延拡張がオンの場合のみ、コマンドはパイプの両側にある括弧付きのブロック、コマンドは「裸の」バッチスクリプト(括弧、CALL、コマンド連結、パイプなしのスクリプト名)。

  • コマンドの各トークンは、遅延拡張のために個別に解析されます。
    • ほとんどのコマンドは、コマンド トークン、引数トークン、および各リダイレクト先トークンの 2 つ以上のトークンを解析します。
    • FOR コマンドは IN 句トークンのみを解析します。
    • IF コマンドは、比較演算子に応じて 1 つまたは 2 つの比較値のみを解析します。
  • 解析されたトークンごとに、まず が含まれているかどうかを確認します!。 が含まれていない場合、トークンは解析されません。これは^文字にとって重要です。 トークンに が含まれている場合は!、各文字を左から右にスキャンします。
    • キャレット(^)の場合、次の文字は特別な意味を持たないため、キャレット自体は削除されます。
    • 感嘆符の場合は、次の感嘆符を検索し(キャレットはもう考慮されません)、変数の値に展開します。
      • 連続した開口部!は1つに折りたたまれます!
      • ペアになっていないものは!削除されます
    • この段階で vars を展開することは「安全」です。なぜなら、特殊文字は検出されなくなるからです ( または<CR><LF>)
    • より詳しい説明については、dbenhamの後半を読んでください。 同じスレッド - 感嘆符フェーズ

フェーズ5.3) パイプ処理:コマンドがパイプのいずれかの側にある場合のみ、
パイプの両側は独立して非同期に処理されます。

  • コマンドが cmd.exe の内部コマンドであるか、バッチ ファイルであるか、または括弧で囲まれたコマンド ブロックである場合は、 を介して新しい cmd.exe スレッドで実行される%comspec% /S /D /c" commandBlock"ため、コマンド ブロックはフェーズの再開を取得しますが、今回はコマンド ライン モードで実行されます。
    • 括弧で囲まれたコマンド ブロックの場合、その<LF>前後にコマンドがあるものはすべて に変換されます<space>&。その他は<LF>削除されます。
  • これでパイプコマンドの処理は終了です。
  • 見るhttps://stackoverflow.com/q/8192318/1012053パイプの解析と処理の詳細については

フェーズ 5.5) リダイレクトの実行:フェーズ 2 で検出されたリダイレクトが実行されます。



ここに画像の説明を入力してください

ここに画像の説明を入力してください

set ar[0]=orange
set ar[1]=apple
set ar[2]=banana
for %i in (0,1,2) do cmd.exe /v:on /C"echo !ar[%i]!

  • あなたの/申告せずに提出するsetlocal enabledelayedexpansion、また、cmd.exe /v:on /c "command & command | command || command..."
@echo off 

set "ar[0]=orange" 
set "ar[1]=apple"
set "ar[2]=banana" 
for %%i in (0,1,2)do cmd /v /c "echo\ !ar[%%i]!"

@echo off 

set "ar[0]=orange"
set "ar[1]=apple"
set "ar[2]=banana" 

setlocal enabledelayedexpansion
for %%i in (0,1,2)do echo\ !ar[%%i]!

enndlocal


@echo off 
set "ar[0]=orange" & set "ar[1]=apple" & set "ar[2]=banana" 
for %%i in (0,1,2)do %ComSpec% /v:on /c"echo !ar[%%i]!"
%__APPDIR__%timeout.exe /t -1 & endlocal & goto :EOF
@echo off 
set "ar[0]=orange" & set "ar[1]=apple" & set "ar[2]=banana" 
setlocal enabledelayedexpansion && for %%i in (0,1,2)do echo\ !ar[%%i]!
%__APPDIR__%timeout.exe /t -1 & endlocal & goto :EOF
@echo off 
set "ar[0]=orange" && set "ar[1]=apple" && set "ar[2]=banana" 
for %%i in (0,1,2)do <con: %ComSpec% /v:on /c"echo !ar[%%i]!"
call <con: rem./ && %__APPDIR__%timeout.exe /t -1 && endlocal
  • あなたも使うことができますcall/またはコマンドラインでこの値を更新します:
set "ar[0]=orange"
set "ar[1]=apple"
set "ar[2]=banana" 
for %i in (0,1,2)do for %i in (0,1,2)do <con: call echo %ar[%i]%


for %i in (0,1,2) do %ComSpec% /v:on /r "echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec% /v:on /c "echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe /v:on /r "echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe /v:on /c "echo !ar[%i]!"

for %i in (0,1,2) do cmd /v:on /r "echo !ar[%i]!"
for %i in (0,1,2) do cmd /v:on /c "echo !ar[%i]!"


for %i in (0,1,2) do %ComSpec% /v /r "echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec% /v /c "echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe /v /r "echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe /v /c "echo !ar[%i]!"

for %i in (0,1,2) do cmd /v /r "echo !ar[%i]!"
for %i in (0,1,2) do cmd /v /c "echo !ar[%i]!"


for %i in (0,1,2) do %ComSpec%/v:on/r "echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec%/v:on/c "echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe/v:on/r "echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe/v:on/c "echo !ar[%i]!"

for %i in (0,1,2) do cmd/v:on/r "echo !ar[%i]!"
for %i in (0,1,2) do cmd/v:on/c "echo !ar[%i]!"

for %i in (0,1,2) do %ComSpec%/v/r "echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec%/v/c "echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe/v/r "echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe/v/c "echo !ar[%i]!"

for %i in (0,1,2) do cmd/v/r "echo !ar[%i]!"
for %i in (0,1,2) do cmd/v/c "echo !ar[%i]!"


for %i in (0,1,2) do %ComSpec%/v:on/r"echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec%/v:on/c"echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe/v:on/r"echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe/v:on/c"echo !ar[%i]!"

for %i in (0,1,2) do cmd/v:on/r"echo !ar[%i]!"
for %i in (0,1,2) do cmd/v:on/c"echo !ar[%i]!"


for %i in (0,1,2) do %ComSpec%/v/r"echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec%/v/c"echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe/v/r"echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe/v/c"echo !ar[%i]!"

for %i in (0,1,2) do cmd/v/r"echo !ar[%i]!"
for %i in (0,1,2) do cmd/v/c"echo !ar[%i]!"


for %i in (0,1,2) do %ComSpec%/v/recho !ar[%i]!
for %i in (0,1,2) do %ComSpec%/v/cecho !ar[%i]!

for %i in (0,1,2) do cmd.exe/v/r"echo !ar[%i]!
for %i in (0,1,2) do cmd.exe/v/c"echo !ar[%i]!

for %i in (0,1,2) do cmd/v/recho !ar[%i]!
for %i in (0,1,2) do cmd/v/cecho !ar[%i]!

答え2

遅延展開は主に、特に For ループ内で変数値を複数回展開するために使用されます。

引用元https://ss64.com/nt/delayedexpansion.html

遅延展開により、バッチ ファイル内の変数は解析時ではなく実行時に展開されます。このオプションは、SETLOCAL EnableDelayedExpansion コマンドでオンになります。

変数の展開とは、変数(例:%windir%)をその値C:\WINDOWSに置き換えることです。

デフォルトでは、展開は各行が実行される前に 1 回だけ行われます。!delayed! 展開は、行が実行されるたびに、または FOR ループ コマンドの各ループごとに実行されます。まず、この例を見てみましょう。

set i=0
for /l %%a in (0,1,10) do (
  set /a i=%i%+1
  echo %i%
)

ここでは、1 から 10 までの数字が出力されることを期待しています。ただし、毎回 0 のみが出力されます。毎回変数が 1 回展開されるため、値は変更されないためです。

今回は遅延拡張を試してみます:

setlocal enabledelayedexpansion 
set i=0
for /l %%a in (0,1,10) do (
  set /a i=!i!+1
  echo !i!
)

今回は、値を展開するたびに値が変更されるため、期待どおりの出力が得られます。

setlocal enabledelayedexpansionコードの先頭に追加する必要があるため、コードは機能しません。

お役に立てれば幸いです

関連情報