題 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


2
2017-08-05 13:04


起源




答案:


你有幾個問題:

  • %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
)

請注意,FOR變量擴展後會發生延遲擴展,因此如果結果將被破壞 %%G 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
)

4
2017-08-05 14:09



哇這很複雜。我想我會堅持使用PowerShell。 - chue x
哇,這是一個精心設計,樂於助人和友好的答案,非常感謝!我真的必須考慮一下並測試我的理解一段時間 - 如果我再次卡住,肯定會回到你和SU。 - beerbear
對不起,戴夫。我不會包括 SET /A environment variable expansion: set /a "value=var+1" 作為本說明書中的步驟編號5),因為該步驟完全取決於所執行的特定命令。我會寫這樣的東西:“某些命令執行變量的進一步擴展 名稱 作為參數給出,就像是的情況一樣 set /P 命令“。恕我直言 - Aacini
@Aacini - 我明白你的觀點,但它是CMD.EXE的固有部分。我相信SET / A是唯一進行自己的變量解析和擴展的內部命令。 - dbenham


davebenham的回答

在FOR或IF等塊中執行echo%var,無法正常工作。 %var%變量只是不更新​​。你必須使用!var!並獲得!var!要工作,你必須setlocal EnableDelayedExpansion

cmd幫助中存在對此的解釋,但是哪個命令的幫助解釋不明顯!它的 set /?

set /?

最後,支持延遲環境變量擴展   添加。默認情況下始終禁用此支持,但可能是   通過/ V命令行切換到CMD.EXE啟用/禁用。見CMD   /?

延遲環境變量擴展對於四處走動非常有用   一線的當前擴張的局限性   讀取文本,而不是執行時。以下示例   演示立即變量擴展的問題:

set VAR=before
if "%VAR%" == "before" (
    set VAR=after
    if "%VAR%" == "after" @echo If you see this, it worked
)

永遠不會顯示消息,因為BOTH IF語句中的%VAR%   在讀取第一個IF語句時替換,因為它在邏輯上   包括IF的主體,這是一個複合語句。所以IF   複合語句裡面真的比較“之前”   “之後”永遠不會平等。同樣,以下示例   不會按預期工作:

set LIST=
for %i in (*) do set LIST=%LIST% %i
echo %LIST%

因為它不會在當前目錄中建立一個文件列表,   但只是將LIST變量設置為找到的最後一個文件。   同樣,這是因為%LIST%在FOR時僅擴展一次   讀取語句,此時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甚至是專家,(也許因為其他東西可以用不同的方式解釋),所以我只是用這個來表示你可以試試!var!用cmd表示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%之內。所以這就是為什麼人們不僅僅是全時轉動模式的原因,可能還有其他原因。


0
2017-08-11 09:08