Windows 批次中的字串替換

Windows 批次中的字串替換

我試圖了解 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% 之內。所以這就是為什麼人們不只是全職開啟該模式的原因,可能還有其他原因。

相關內容