延遲擴充在批次腳本中如何運作?

延遲擴充在批次腳本中如何運作?

在批次環境中執行這一系列簡單的指令:

>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

資料來源:
第五階段Windows 命令解釋器 (CMD.EXE) 如何解析腳本?

延遲擴展:僅當延遲擴展打開時,該命令才不在管道兩側的括號塊,並且該命令不是「裸」批次腳本(不含括號、CALL、指令連線或管道的腳本名稱)。

  • 命令的每個標記都會被獨立解析以進行延遲擴展。
    • 大多數命令解析兩個或多個標記 - 命令標記、參數標記和每個重定向目標標記。
    • FOR 指令僅解析 IN 子句標記。
    • IF 指令僅解析比較值 - 一個或兩個,取決於比較運算子。
  • 對於每個解析的令牌,首先檢查它是否包含任何!.如果不是,則不會解析令牌 - 對於^字元來說很重要。如果令牌確實包含!,則從左到右掃描每個字元:
    • 如果它是插入符號 ( ^),則下一個字元沒有特殊意義,插入符號本身將被刪除
    • 如果是感嘆號,則搜尋下一個感嘆號(不再觀察到插入符號),擴展到變數的值。
      • 連續的開口!被折疊成一個!
      • 任何剩餘的未配對!將被刪除
    • 在這個階段擴展變數是“安全的”,因為不再檢測到特殊字元(甚至<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 階段中發現的任何重新導向。



在此輸入影像描述

  • 您也可以使用Delayed Expansion經過cmd.exe有旗幟[/v:on | /v],在或在/文件。

在此輸入影像描述

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]!"

  • 在你的/宣告setlocal enabledelayedexpansion:
@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]%


  • 在你的/文件或命令行,以下所有命令都相同,觀察替換%i%%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 命令開啟。

變數擴充意味著用 C:\WINDOWS 的值取代變數(例如 %windir%)

預設情況下,在執行每行之前,擴充只會發生一次。 !每次執行該行時都會執行擴展,或者對於 FOR 循環命令中的每個循環都會執行擴展。首先我們來看這個例子:

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

這裡我們期望輸出數字1到10。

這次我們將嘗試延遲擴充:

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

這次我們將得到預期的輸出,因為每次我們擴展值並且值都會改變。

您的代碼不起作用,因為您必須setlocal enabledelayedexpansion在代碼開頭添加。

希望有幫助

相關內容