#contents //////////////////////////////////////////////////////////////////////////// *for /f %A in ('command ....') の日本語処理のバグ (W2K) [#gd9e7e3e] for /f %%A in ('echo あいう123456789') do echo %%A for /f %%A in ('echo あ123456789') do echo %%A では、それぞれ、「あいう123456789」「あ123456789」が表示されるはずだが、実際には、「あいう123456」「あ12345678」が表示される。(' ') の2バイト文字の数だけ (' ') 内のコマンド末尾から文字が削られるようだ。かといって空白を末尾につけても無駄で同じ結果に終わる。空白だけでなく、セミコロン、カンマ、イコールなどの in ( ) 内リストの区切り文字も駄目。また、usebackq と ` ` を使った場合も同じである。 WinXPでは直っているのにWin2000の最新SPでも直っていないということは、直す気が無いということだろう。初歩的過ぎて情けないバグである。 現実的には、 for /f "delims=" %%A in ('find "日本語" filename') do .... のようなケースで問題になるだろう。2バイト文字の数が分かっていれば、 for /f "delims=" %%A in ('find "日本語" filename@@@') do .... のようにその数だけ余分の文字をあらかじめ末尾に付けて置けば良いが、2バイト文字の数が不明の場合(及びWin2000とWinXPで共通に使う場合)は、例えば、 for /f "delims=" %%A in ('find "%STR%" filename^|findstr /v qwertyuiopasdfghjkl') do .... のように末尾からある程度文字が消えても良いようにする必要がある。何か情けない方法である。 #comment() //////////////////////////////////////////////////////////////////////////// *%~sA のバグ [#c467a212] for変数や、バッチ引数には、%~sA や %~s1 という修飾子でショートネーム(または8.3形式。以下ではSFNと書く)形式のパス名・ファイル名を得ることが出来る。しかしこれにはバグがある。NT4時代からあって、直っていないらしい。このバグは簡単に再現できる。 md "long long long dir" cd "long long long dir" echo.>"A B.txt" for %%A in (*.txt) do echo %%~sA 本来は、「\LONGLO~1\ABBE64~1.TXT」 のように表示されるべき部分が、「\LONGLO~1\ABBE64~1.TXTB.txt」 のように後ろにごみ(LFNの残骸)が付いてしまう。 こういうバグがある以上、特定の環境で一時的に使うバッチならともかく、汎用ツールを作る時は ~s 修飾子を安易に使えないと言うことだ。 解決法としては、パスの上位部分から順にSFN形式にしていくしか無いようだ。汎用サブルーチン :sfnsub とその(上記スクリプトで作ったファイルでの)テストスクリプトを示す。 :sfnsub の仕様としては、" "で囲んだフルパスを引数にすると、環境変数 SFN にそのSFNを返す。" "で引数を囲むのは必須。 @echo off setlocal rem サンプルメイン for %%A in ("A B.txt") do echo %%~sA&call :sfnsub "%%~fA" echo %SFN% goto :eof rem サブルーチン :sfnsub set ARG=%1 set ARG=%ARG:\=\" "% set SFN= call :loop %ARG% goto :eof :loop for %%A in ("%SFN%%~1") do set SFN=%%~sA shift if not "%~1"=="" goto :loop set "SFN=%SFN:&=^&%" set "SFN=%SFN:^^=^%" goto :eof 汎用と書いたが、% はスクリプト中でサブルーチンの引数として渡せない((もちろん、% を %%%% に置換してから引数に使えばよいが、for変数には置換が使えないし、置換するために一般の環境変数に一旦セットすればさらに多くの問題を引き起こすので現実的でない))ので % を含んだファイル名は駄目だがそれ以外のファイル名に使える特殊文字には対応しているはずだ(まだ漏れがあるかもしれないが)。 なお、この方法と別に COMMAND.COM を使ってカレントディレクトリを SFN に変換する別法もある。 @echo off setlocal for %%A in ("A B.txt") do echo %%~sA&call :sfnsub "%%~fA" echo %SFN% goto :eof :sfnsub pushd . cd /d "%~dp1" command /c rem for %%A in ("%~nx1") do set SFN=%%~sA popd goto :eof こっちのほうがわかりやすい。ただし、日本語版Windowsの場合は初回のCOMMAND.COM実行時に画面がクリアされていろいろメッセージが出る((英語版では出ないらしい。これを出さない方法は無いだろうか?))のでいまいちである。 いずれにせよたかがSFNを得るために大げさな話であり、スクリプトではSFNを使うのを避けたほうがいいだろう。 \Program files\ の下だけ調べてみたが、%~sA が正しい結果を返さないファイルは結構 ある。 #comment() ////////////////////////////////////////////////////////////////////////////