web-dev-qa-db-ja.com

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

しかし、これはそうではありません(ディレクトリ「メイン」の例):

@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

3
beerbear

あなたにはいくつかの問題があります:

  • _%var%_展開は、ステートメントが解析されるときに発生し、コマンドが実行される前に、括弧で囲まれたコードブロック全体が1回のパスで解析されます。したがって、値はループが開始される前に存在していた値です。解決策は、ループ内の各コマンドが実行されるときに発生する遅延拡張です。

  • あなたの論理は間違っています-_newpの割り当ては_srcpの値に基づくべきです

CMDプロセッサは複雑な獣です (そして文書化も不十分です)。さまざまな種類の変数が展開されるポイントは複数あります。バッチプログラミングを本当に最大限に活用したい場合は、それらを完全に理解する必要があります。それはすべてリンクで説明されていますが、要約すると、拡張の順序は次のとおりです。

1)%展開-パラメーター:_echo %1_または環境変数:_echo %var%_
----ほとんどの解析はこれまでに完了しています----
2)変数展開の場合:for %%A in (*) do echo %%A
3)環境変数の拡張の遅延:_echo !var!_
4)CALL%expansion-パラメーター:_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_に_!_が含まれていると、結果が破損することに注意してください。これは、追加の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
)
_

2パーセントの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
)
_
5
dbenham

さらにデイブベンハムの答えに

fORやIFなどのブロック内でecho%var%を実行すると、正しく機能しません。 %var%変数は更新されません。 !var!を使用する必要があります。そして!var!動作するには、ローカルの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ステートメントが読み取られるときに両方のIFステートメントの%VAR%が置換されるため、メッセージは表示されません。したがって、複合ステートメント内のIFは、実際には「前」と「後」を比較していますが、これは決して等しくなることはありません。同様に、次の例は期待どおりに機能しません。

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

現在のディレクトリにファイルのリストを作成するのではなく、最後に見つかったファイルにLIST変数を設定するだけです。繰り返しますが、これは、FORステートメントが読み取られたときに%LIST%が1回だけ展開され、その時点で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内の表記。

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
barlop