
我試圖了解 Windows 批次中的字串替換實際上是如何工作的,但遇到了麻煩。
@echo off
set var=wild
set varnew=%var:l=n%
echo var is: %var%
echo varnew is: %varnew%
確實有效;它產生預期的輸出:
var is: wild
varnew is: wind
但這不是(範例目錄“Main”):
@echo off
for /D %%G IN (*) do (
setlocal
echo G is: %%G
set _srcp=%%G
echo _srcp is %_srcp%
rem set _newp=%_newp:ai=_01_% <-- confused variable
set _newp=%_srcp:ai=_01_%
echo._newp is: %_newp%
endlocal
)
它產生以下輸出:
G is: Main
_srcp is Main
_newp is: %_srcp:ai=_01_
我希望程式碼產生為_newp is: M_01_n
最後一行。我真的沒有想法,有人可以指出我正確的方向嗎?
BB
答案1
你有幾個問題:
%var%
解析語句時會發生擴展,並且在執行任何命令之前一次解析整個帶括號的程式碼區塊。所以該值是循環開始之前存在的值。解決方案是延遲擴展,這是在執行循環內的每個命令時發生的。你的邏輯是錯誤的 - _newp 的分配應該基於 _srcp 的值
CMD 處理器是個複雜的野獸(而且記錄也很少)。各種類型的變數有多個擴展點,如果您真正想充分利用批次編程,則必須充分理解它們。連結中對此進行了全部解釋,但總的來說,擴展順序是:
1) % 擴展 - 參數:echo %1
或者環境變數:echo %var%
---- 目前大部分解析已完成 ----
2) FOR 變數擴充:for %%A in (*) do echo %%A
3) 延遲環境變數擴充:echo !var!
4) CALL % 擴充 - 參數:call echo %%1
或者環境變數:call echo %%var%%
5) SET /A 環境變數擴充: `set /a "value=var+1"
請注意,延遲擴展需要透過以下方式啟用延遲擴展SETLOCAL EnableDelayedExpansion
以下使用延遲擴展的程式碼將給出您尋求的結果:
@echo off
for /D %%G in (*) do (
setlocal enableDelayedExpansion
echo G is: %%G
set "_srcp=%%G"
echo _srcp is !_srcp!
set "_newp=!_srcp:ai=_01_!"
echo _newp is: !_newp!
endlocal
)
%%G
請注意,延遲擴展發生在 FOR 變數擴展之後,因此如果constains ,結果將被損壞!
。這可以透過附加 SETLOCAL 來避免:
for /D %%G in (*) do (
setlocal disableDelayedExpansion
echo G is: %%G
set "_srcp=%%G"
setlocal enableDelayedExpansion
echo _srcp is !_srcp!
set "_newp=!_srcp:ai=_01_!"
echo _newp is: !_newp!
endlocal
endlocal
)
您也可以使用帶有雙百分號的 CALL 來獲得所需的結果,但這要慢得多。如果執行幾次,速度並不重要,但如果在循環中執行數千次,速度就變得非常重要。
@echo off
for /D %%G in (*) do (
setlocal
echo G is: %%G
set "_srcp=%%G"
call echo _srcp is %%_srcp%%
call set "_newp=%%_srcp:ai=_01_%%"
call echo _newp is: %%_newp%%
endlocal
)
答案2
進一步達維本納姆的回答
在 FOR 或 IF 等區塊內執行 echo %var% 無法正常運作。 %var% 變數只是不更新。你必須使用!並得到 !var!要工作,您必須設定local EnableDelayedExpansion
cmd 幫助中有對此的解釋,儘管不清楚哪個命令的幫助解釋了這一點!它是set /?
set /?
最後,新增了對延遲環境變數擴展的支援。預設情況下,此支援始終處於停用狀態,但可以透過 CMD.EXE 的 /V 命令列開關來啟用/停用。參見CMD /?
延遲環境變數擴展對於解決當前擴展的限制很有用,當前擴展是在讀取一行文字時而不是在執行文字時發生的。以下範例示範了立即變數擴充的問題:
set VAR=before if "%VAR%" == "before" ( set VAR=after if "%VAR%" == "after" @echo If you see this, it worked )
永遠不會顯示該訊息,因為當讀取第一個 IF 語句時,兩個 IF 語句中的 %VAR% 都會被替換,因為它在邏輯上包含 IF 的主體,而 IF 是一個複合語句。因此,複合語句中的 IF 實際上是在比較“之前”和“之後”,它們永遠不會相等。同樣,以下範例也不會如預期般運作:
set LIST= for %i in (*) do set LIST=%LIST% %i echo %LIST%
因為它不會在當前目錄中建立文件列表,而只是將 LIST 變數設定為找到的最後一個檔案。同樣,這是因為讀取 FOR 語句時 %LIST% 僅擴展一次,而此時 LIST 變數為空。所以我們執行的實際 FOR 迴圈是:
for %i in (*) do set LIST= %i
它只是將 LIST 設定為最後找到的檔案。
延遲環境變數擴充可讓您在執行時使用不同的字元(感嘆號)來擴展環境變數。如果啟用了延遲變數擴展,則上面的範例可以編寫如下以按預期工作:
set VAR=before if "%VAR%" == "before" ( set VAR=after if "!VAR!" == "after" @echo If you see this, it worked ) set LIST= for %i in (*) do set LIST=!LIST! %i echo %LIST%
您也可以使用命令列測試符號
cmd /e:on
從預設值開始, cmd /v:off 然後移動到 cmd/v:on 大多數人甚至專家都使用 cmd .v:off (也許因為其他東西可以用它來解釋),所以我只是用這個來展示你可以試試看! cmd 內用它表示法。
C:\>set a=5
C:\>echo %a%
5
C:\>echo !a!
!a!
C:\>cmd /v:on
Microsoft Wind
Copyright (c)
C:\>echo !a!
5
C:\>
順便說一句,如果您有 cmd /v:on 或在批次檔中啟用 EnableDelayedExpansion,那麼正如 Dave 所示,您必須注意!作為一個特殊角色,所以如果 !在 %var% 之內。所以這就是為什麼人們不只是全職開啟該模式的原因,可能還有其他原因。