[前の記事]« コンピュータ名を取得してログオンするバッチ
[次の記事]書き込まれた数値が条件に合致する行だけ別ファイルに出力したい »

関連オンラインソフト配布サイト:
Vector>ダウンロード(top) |>MS-DOSまたは汎用 | >WindowsVista/XP/2000/NT

▲カテゴリー「FOR .. IN ( ) DO..」索引へ
▲カテゴリー「遅延環境変数の展開」索引へ

FORでの処理を、パラメータに「%」や「!」があっても誤動作しないようにしたい

653.
FORと遅延環境変数の展開について
2005年07月31日(日) 14時52分
こぶん
●DOS/Windowsのバージョン↓
Microsoft Windows XP [Version 5.1.2600]

forにてファイルやフォルダの一覧を取得して処理しようとすると、遅延環境変数の展開を有効にしていると ! が消えてしまいます。また遅延環境変数の展開を無効にしていると % などがおかしくなります。両方の特殊文字に対応した処理の方法についてお知恵をお貸し下さい。

以下に掲げるサンプルを例えばa.cmdとして保存し
C:\>a a!b%c.d
と実行するとわかりやすいと思います

@echo off

:test
echo 遅延環境変数の展開は無効
setlocal disabledelayedexpansion

echo OKだが再帰処理や関数的処理には向かないのでNG
for %%F in ("%~1") do echo %%F

echo 対象に%%があると困る
for %%F in ("%~1") do call :check %%F

echo callする段階で対象に%%があると困る
for %%F in ("%~1") do (
echo %%F
call :check %%F
)

echo ループ内部で遅延を有効にした時点で対象の!が消失
for %%F in ("%~1") do (
echo %%F
setlocal enabledelayedexpansion
echo %%F
set S1="%%~F"
set S1=!S1:%%=%%%%!
call :check !S1!
endlocal
)

echo 遅延環境変数の展開を有効
setlocal enabledelayedexpansion
echo 対象に^^!があると消えてる
for %%F in ("%~1") do echo %%F

echo 対象に^^!や%%があると^^!がまず消え、callすると%%が消える
for %%F in ("%~1") do (
echo %%F
call :check %%F
)

echo 対象に^^!があるとループ内容に突入した時点で消えてるので処理しようが…
for %%F in ("%~1") do (
echo %%F
set S2="%%~F"
set S2=!S2:%%=%%%%!
call :check !S2!
)
endlocal
goto :eof

:check
echo "check %1"
656.
Re:FORと遅延環境変数の展開について
2005年08月01日(月) 15時22分
otn
>●DOS/Windowsのバージョン↓
>Microsoft Windows XP [Version 5.1.2600]
>
>forにてファイルやフォルダの一覧を取得して処理しようとすると、遅延環境変数の展開を有効にしていると ! が消えてしまいます。また遅延環境変数の展開を無効にしていると % などがおかしくなります。両方の特殊文字に対応した処理の方法についてお知恵をお貸し下さい。

forの中で複雑な処理をしたいので、
・( ) に囲んで遅延展開を使う
・do の後 call でサブルーチンを使ってその中で処理する
のいずれかを選択しないといけないと言うことだと思いますが、

あと、出てないパターンとしては、遅延展開を使わず、
for /f "なんとか" %%A in (かんとか) do (
 echo %%A>tmpfile.tmp
 call :sub
)
goto :eof
:sub
set /p VAR=<tmpfile.tmp

みたいに、引数でなくてファイル経由でサブルーチンに文字列を渡す。

それで機能が足りなければ、バッチスクリプトの限界で、vbs等他のスクリプト言語を頼る事になると思います。
658.
Re:FORと遅延環境変数の展開について
2005年08月01日(月) 16時11分
otn
>>●DOS/Windowsのバージョン↓
>>Microsoft Windows XP [Version 5.1.2600]
>>
>>forにてファイルやフォルダの一覧を取得して処理しようとすると、遅延環境変数の展開を有効にしていると ! が消えてしまいます。また遅延環境変数の展開を無効にしていると % などがおかしくなります。両方の特殊文字に対応した処理の方法についてお知恵をお貸し下さい。
>
>forの中で複雑な処理をしたいので、
>・( ) に囲んで遅延展開を使う
>・do の後 call でサブルーチンを使ってその中で処理する
>のいずれかを選択しないといけないと言うことだと思いますが、
>
>あと、出てないパターンとしては、遅延展開を使わず、
>for /f "なんとか" %%A in (かんとか) do (
> echo %%A>tmpfile.tmp
> call :sub
>)
>goto :eof
>:sub
>set /p VAR=<tmpfile.tmp
>
>みたいに、引数でなくてファイル経由でサブルーチンに文字列を渡す。

ありゃ、これだったら、下記で良いですね。
for /f "なんとか" %%A in (かんとか) do (
 set VAR=%%A
 call :sub
)
goto :eof
:sub

ファイルを使わないといけない目に会ったのは何だったか調べると、もっと特殊なケースでした。
659.
Re:FORと遅延環境変数の展開について
2005年08月01日(月) 16時49分
こぶん
再帰処理をするためにパラメーター渡しをしていたので、一意な一時ファイルを作って、その一時ファイル名を渡すようにしたのですが…ファイル名に関する回避はできたのですが、あまりにも大きな速度低下してしまいました…
実用にならないのでWSHかCで組むことにします。
660.
Re:FORと遅延環境変数の展開について
2005年08月01日(月) 16時58分
otn
>再帰処理をするためにパラメーター渡しをしていたので、一意な一時ファイルを作って、その一時ファイル名を渡すようにしたのですが…ファイル名に関する回避はできたのですが、あまりにも大きな速度低下してしまいました…
>実用にならないのでWSHかCで組むことにします。

再帰処理ですか。差し支えなければ見てみたいです。
661.
Re:FORと遅延環境変数の展開について
2005年08月01日(月) 17時47分
こぶん
>再帰処理ですか。差し支えなければ見てみたいです。

実際には対したことはやっていません、下位ディレクトリを全処理するためだけの再帰です。
コピーエンジンプログラムだけCで作ってフロントエンドにバッチでcopy xcopy move 相当のものを用意して使おうとしてファイル名でハマったというわけです。
以下のバッチは%ハックなどを入れる前の原型で、move用です。(こっちの方が見やすいかと思いまして、でも原型に戻す際にどっかミスってるかも^^)

@echo off
setlocal

rem 定義
set DIRFILE=dir /A:-D /B
set DIRDIR=dir /A:D /B

rem 引数チェックとusage
(全省略)

rem カレントフォルダ
:top
if %DEBUG%=1 (
echo top "%~1" "%~2" >&2
)
rem ファイル
for /F %%F in ('%DIRFILE% "%~1"') do (
call :vcp "%%~F" "%~2"
)
rem フォルダ&ジャンクション
for /F %%R in ('%DIRDIR% "%~1"') do (
if not EXIST "%~2\%%~nxR" mkdir "%~2\%%~nxR"
call :refl "%%~fR" "%~2\%%~nxR" && rmdir "%%~fR" || echo フォルダ "%%~fR" を削除できません(top) >&2
)
endlocal
goto :eof


rem 下位フォルダ(再帰)
:refl
if %DEBUG%=1 (
echo refl "%~1" "%~2" >&2
)
rem ファイル
for /F %%F in ('%DIRFILE% "%~1\*.*"') do (
call :vcp "%%~F" "%~2"
)
rem フォルダ&ジャンクション
for /F %%R in ('%DIRDIR% "%~1\*.*"') do (
if not EXIST "%~2\%%~nxR" mkdir "%~2\%%~nxR"
call :refl "%%~fR" "%~2\%%~nxR" && rmdir "%%~fR" || echo フォルダ "%%~fR" を削除できません(refl) >&2
)
goto :eof

rem コピー部
:vcp
call :isFolder "%~1"
if %ERRORLEVEL%==1 (
if %DEBUG%==1 (
@echo "%~1" を "%~2\%~nx1" にコピーします >&2
)
rem NTFS→FAT考慮
rem FAT:サブストリームを通常ファイル化、制限サイズでスプリット
rem NTFS:そのままサブストリームごとコピー
rem 共通:インクリメンタルベリファイ&リトライ
@"%~dp0vcopy.exe" "%~1" "%~2\%~nx1" && del /F "%~1" || echo 削除できません "%~1" >&2
)
goto :eof

rem フォルダチェック 0でフォルダ 1でファイル
:isFolder
set isFolder=1
pushd .
cd /d "%~1" 2>NUL && set isFolder=0
popd
exit /b %isFolder%
662.
Re:FORと遅延環境変数の展開について
2005年08月01日(月) 20時08分
otn
>>再帰処理ですか。差し支えなければ見てみたいです。
>
>実際には対したことはやっていません、下位ディレクトリを全処理するためだけの再帰です。
>コピーエンジンプログラムだけCで作ってフロントエンドにバッチでcopy xcopy move 相当のものを用意して使おうとしてファイル名でハマったというわけです。
>以下のバッチは%ハックなどを入れる前の原型で、move用です。(こっちの方が見やすいかと思いまして、でも原型に戻す際にどっかミスってるかも^^)

確かに、再帰呼び出しだと、固定の環境変数を使ってのサブルーチンへの値渡しが出来ませんね。
ただ、:vcp は再帰呼び出しされないので、
set V1="%~1\%%~F"
set V2="%~2\%%~nxF"
call :vcp
のように出来ますよね。

:refl の方は、pushd/popdを使って引数なしに出来ないかと思いましたが、難しそうです。
663.
Re:FORと遅延環境変数の展開について
2005年08月01日(月) 21時48分
otn
>:refl の方は、pushd/popdを使って引数なしに出来ないかと思いましたが、難しそうです。

こんな感じで。

@echo off
setlocal

rem 定義
set "DIRFILE=dir /A:-D /B 2^>NUL"
set "DIRDIR=dir /A:D /B 2^>NUL"
set TMPF="%TEMP%\tmpfile.bat"

rem 引数チェックとusage
rem (全省略)

rem カレントフォルダ
:top
if %DEBUG%==1 (
 echo top "%~f1" "%~f2" >&2
)
echo set DEST=%%CD:%~f1=%~f2%%>%TMPF%
pushd "%~1"
call :refl
popd
endlocal
goto :eof


rem 下位フォルダ(再帰)
:refl
call %TMPF%
if %DEBUG%==1 (
 echo refl "%CD%" "%DEST%" >&2
)
rem ファイル
for /F "delims=" %%F in ('%DIRFILE%') do (
 set V1="%CD%\%%~F"
 set V2="%DEST%\%%~nxF"
 call :vcp
)
rem フォルダ&ジャンクション
for /F "delims=" %%R in ('%DIRDIR%') do (
 if not EXIST "%DEST%\%%~nxR" echo mkdir "%DEST%\%%~nxR"
 pushd "%%~R"
 call :refl
 popd
 if %ERRORLEVEL%==0 echo rmdir "%%~fR"
)
goto :eof

rem コピー部
:vcp
dir /a:d %V1% >NUL 2>NUL
if %ERRORLEVEL%==1 (
 if %DEBUG%==1 (
  @echo %V1% を %V2% にコピーします >&2
 )
 echo copy %V1% %V2%
 ver>NUL
)
goto :eof

rmdirの条件はこれでいいのかどうか。
666.
Re:FORと遅延環境変数の展開について
2005年08月02日(火) 13時32分
こぶん
ざっと見させて頂きました。

>echo set DEST=%%CD:%~f1=%~f2%%>%TMPF%
ここ、マズいと思います。
a.batと保存したとして
C:\test folder\a!b%c.d>a.bat a!b%c.d a%b!c.d
と実行したとすると
set DEST=%CD:C:\test folder\a%b!c.d\a%b!c.d=C:\test folder\a%b!c.d\a!b%c.d%
のようになってしまい
>call %TMPF%
を初回実行した直後にDESTの内容が C:\test folder\ab!c.d\a!b になってしまうと思います。(何個の%を使って置換すればいいのか混乱してきた;(

あと、一時ファイルなんですが
:tmpfile
for /F "tokens=1,2,1-3 delims=/" %%D in ("%DATE%") do set DT=%%D%%E%%F
for /F "tokens=1,2,1-4 delims=:." %%T in ("%TIME%") do set TM=%%T%%U%%V%%W
if not exist "%TEMP%\%~n0%DT%" mkdir "%TEMP%\%~n0%DT%"
set TMPF="%TEMP%\%~n0%DT%\cmd%COMPUTERNAME%%TM%%RANDOM%.cmd"
if exist %TMPF% goto :tmpfile
echo. >%TMPF%
とすると一応一意な名前でファイルを作れるのですが、もっと良い方法ありませんでしょうかね?
667.
Re:FORと遅延環境変数の展開について
2005年08月02日(火) 14時59分
otn
>ざっと見させて頂きました。
>
>>echo set DEST=%%CD:%~f1=%~f2%%>%TMPF%
>ここ、マズいと思います。
>a.batと保存したとして
>C:\test folder\a!b%c.d>a.bat a!b%c.d a%b!c.d
>と実行したとすると

コピー対象のファイル・ディレクトリだけでなく、引数にも%が出て来るケースまでは想定の範囲外でした。
手動での起動なら、実用を考えると、そのときだけリネームするというのもあると思います。
あとは、tmpfile.bat を vbs 化するか。

>あと、一時ファイルなんですが
。。。
>とすると一応一意な名前でファイルを作れるのですが、もっと良い方法ありませんでしょうかね?

再帰呼び出しに使うだけなら連番でよいと思っていましたが、時刻やRANDOMを使っているということはこの処理が多重起動されるケースまで考えないといけないということでしょうか?
それなら抜本的な改善案は思いつきません。
668.
Re:FORと遅延環境変数の展開について
2005年08月02日(火) 15時41分
こぶん
>コピー対象のファイル・ディレクトリだけでなく、引数にも%が出て来るケースまでは想定の範囲外でした。

どんな引数が来ても大丈夫なように作成するのは困難であるとわかっただけでも大きな収穫でした。

>再帰呼び出しに使うだけなら連番でよいと思っていましたが、時刻やRANDOMを使っているということはこの処理が多重起動されるケースまで考えないといけないということでしょうか?

そうです、標準コマンドの代替として使える程度のものを想定しておりました。

結局、使い慣れたCで作成することにします。バッチでちょちょいと作れると考えた私が浅はかでした。

長々とお付き合い頂き有難う御座いました。
 
 


カテゴリー「FOR .. IN ( ) DO..」索引へ
カテゴリー「遅延環境変数の展開」索引へ

 

カテゴリー一覧

FPCU/FPCDIY QUICK LINK online
この上の各ジャンル名をクリックすると、下のウィンドウが切り替わります▼


=リンク集のインデックスはこちら=

資料書籍

About

2005年07月31日 15:34に投稿されたエントリーのページです。

ひとつ前の投稿は「コンピュータ名を取得してログオンするバッチ」です。

次の投稿は「書き込まれた数値が条件に合致する行だけ別ファイルに出力したい」です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

This site is powered by Six Apart
Movable Type 3.33-ja
このブログについて | 運営:有限会社メディアポート