ファイルエクスプローラからバッチファイルへドラッグ&ドロップして正しいファイル名を得る方法

Windowsのコマンドプロンプトで使うバッチファイルはうまく使えばお手軽で便利です。簡単な処理の自動化にはもってこいです。
しかしバッチへファイルをドラッグ&ドロップをした時に困る事があります。それはファイル名に全角が使われているとバッチファイルへ正しくファイル名を引数として渡せない事です。これは全角スペースが主な原因です。バッチではファイル名の全角スペースを引数の区切と誤認識してしまうのです。
このシンプルですが厄介な問題は未だに多くの方が手こずっているようです。



バッチへ引数を渡す時に引用符(”)で括ってあげれば引数に半角スペースがあろうが全角スペースがあろうが正しい引数を渡すことができます。しかし、ファイルエクスプローラーでファイルを選択しバッチへドラッグ&ドロップした場合は微妙な引数が渡されてしまうのです。それは引用符で括られたり括られなかったりするのです。

その違いはどこから発生するのかというとパス名およびファイル名に半角スペースが入っていると引用符で括られます。半角スペースが入っていないと引用符で括られる事はありません。
そのため、半角スペースの無いパスとファイル名で ファイル名に全角スペースが使われているファイルの場合、バッチへドラッグ&ドロップすると全角スペースが引数の区切と認識され一つのファイルパスが2つの引数に分かれてしまいます。
基本的にはWindowsのコマンドプロンプトの仕様のバグではないか? という感じな問題です。バッチが全角スペースを区切として認識しなければ何の問題も生じなかったことでしょう。

話を簡単にするため全角スペースだけを取り上げましたがカンマ(,) イコール(=)とセミコロン(;)も区切文字として認識されてしまうそうです。

この問題の解決策としてはパスに必ず半角スペースを入れる消極的解決方法があります。半角スペースを入れることでファイルエクスプローラが引用符で括った引数をバッチへ渡してくれますので問題が生じません。この解決方法はこういったお約束が判っている方にしか使えないバッチになり万人向けのバッチとは言えないのが難点です。

文字コードにより生じる問題はパソコン黎明期から起きています。当時は7bit文字圏以外の国は日本くらいしかなかったですけど。しかし今では、インターネットで世界中どこでも起きている問題です。HTMLでホームページを書いた事がある方なら思った文字を表示できなくて悩んだ事ぐらいあるでしょう。

ならば解決策も似たような方法で良いはず。と思いながら検索すると"0xC000013A"様のブログへたどり着きました。
パス名にカンマやセミコロンを含むファイルをバッチファイルに「送る」場合の問題と対策。 (片っ端から忘れていけばいいじゃない。
解決のため2段構えの構造になっています。まずは引数全体で問題となる文字をエスケープ文字へ置き換えます。そしてエスケープ文字になった引数全体を別のバッチへ渡します。渡されたバッチでは問題が起こる文字がエスケープされているのでファイル名の区切の問題が起きません。正しいファイル単位で引数が取り出せます。しかし、本来の文字がエスケープ文字に置き換えられているためこのままでは正しいファイル名ではありません。最後にエスケープ文字を本来の文字へ戻してやれば元のファイル名を正しく取得できるというわけです。

とても良い方法だと思います。今まで苦労していたファイルのD&Dが正しく動いてくれます。多くの原因は全角スペースなのでこれが回避できるだけでD&Dが動くようになりました。

"0xC000013A"様のブログではバッチファイルを2つに分けています。2つペアのファイルを管理するのは少し面倒です。ちょっと試したところラベルを使ったサブルーチンCALLで問題なく動くことが判りました。これならバッチの頭におまじない的に書いておく定型文にする事ができます。

しかしながらより完全を目指すとまだ使えないファイル名があります。次は"=(EQUAL)"と"%(PERCENT)"記号も使いたいです。"0xC000013A"様と同じ方法で拡張しようとしても"="の置き換えは正しく動かず、"%"の置き換えはできてもダブルクォーテーションで括ってcallの引数で渡す事ができないためメインの処理で使う直前にエスケープを元に戻す必要がありました。

  • SET "ARG=%ARG:==$equal$%"
    • 解釈されない
  • SET "ARG=!ARG1:%%=$percent$!
    •  setlocal EnableDelayedExpansionを使う。エスケープしないと引数で渡せない
  • SET "ARG=!ARG1:$percent$=%%!"
    • メイン処理でエスケープを戻す 

ちょっと行き詰まり感が。変数の文字列置き換えはWindowsのバージョンで解釈が違うようなので汎用性もそれほど無い感じです。他の方法が必要です。

一文字づつ解釈してエスケープするしかないかな?と思いながら検索していると"百合亞"様のページにたどり着きました。
バッチでセミコロンやキャレットなどの記号を含む引数を処理するスモキンカゴーン
早速試したところD&Dできるファイル名が増えました。ファイル名を得るだけで複雑なコードになってきましたが。
"=(EQUAL)", "%(PERCENT)", "!(EXCLAMATION)",  "'(SINGLE QUOTATION)"なども使えるようになりました。
これらの文字が使える代わりに引数の便利な書式"%~dp1"など引数のパス文字列を得る命令が使えなくなります。

しかしどうやっても絶対にD&Dできない文字は残ってしまいます。それはコマンドラインで特殊な意味を持つ文字です。そもそも引数として渡される事が無い文字です。
"&(AMPASAND)",  "^(CARET)"などです。
こんな文字がファイル名に使えてしまうのはWIndowsの仕様がおかしいというだけですが。"|(VERTICAL BAR)"みたいに最初から使えなければいいのに。

この問題は2バイト文字圏だけでなく7bit文字の人たちも引っかかる事です。そんな人たちは困らないのかなぁ?と検索するとやはり困っているようです。
Drag & drop files to batch (Computer Tech Support Forum - Windows - Linux - Mac - Computing.Net)
こちらは置き換え方式で解決を図ってますね。"^ (CARET)"でエスケープしています。Windows 8.1のCMD.EXEじゃ動かないのですが2009年ってWindows XPでしたっけ?

で結局、ファイルエクスプローラで扱えるすべてのファイル名をバッチファイルへドラッグ&ドロップする事はできません。英語フォーラムの最後のコメントがあるようにスクリプト言語を使うしかありません。VBScript, JavaScript, Windows Scripting Hostなどです。どれを使おうが.NET Frameworkを使う事になるので同じですね。するとユーザーアカウント制御が邪魔くさいですけど。

多くの場合は"0xC000013A"様の方式で解決すると思います。するとコマンドラインのパス分解が使える手軽さを保てて良いと思います。

コメント

最近のコメント

Threaded Recent Comments will be here.