2005-05-15
ここ数年は、メーラとして EdMax を愛用しています。 EdMax は、添付ファイルをデコードして取り出し、Attachmentフォルダに日時ベースの子フォルダを作成して格納して管理します。
メールを複製したときに添付ファイルが重複しないので使用容量が節約できるというメリットが考えられる仕様ですが、実際のところ複製なんてしないのであまり関係ありません。 むしろウィルス入り添付ファイルを受信した時に、問題のある添付ファイル「だけ」が検閲されるという点がメリットかも知れません。 メール本文と添付ファイルをくっつけた状態で管理していると、ウィルスに関係のない本文や他の添付ファイルまで隔離されてしまい、参照できなくなってしまいます。 メーラによっては、過去のメールまで参照できなくなって慌てる事になるかも。
その一方で、作成した添付ファイルフォルダが消されずに残ってしまう事が多いのです。 そう言う操作をしたからなのか、不具合なのかは分からないのですが。 以前から気にはなっていたので、Attachmentフォルダを整理してみる事にしました。
一般的には、空のフォルダを削除するツールを使って削除するのが簡単かつ確実でしょう。 たとえば DelPath とか。 でもそれでは面白くないので、今回はバッチファイルで頑張ってみる事にします。 今の環境だと、OS は WindowsXP 。 バッチコマンドの拡張機能を使うので、古いヤツでは使えないかも。
単純に空のフォルダを削除してもいいんですが、少しだけ拡張してみます。 具体的には、次のファイルを削除する処理を追加します。
サイズが 0Byte のファイル
Norton AntiVirus の検閲済添付ファイル
贅沢を言えば、Attach$_01.html といった名前のファイルも削除できると便利なのです。 ただし、内容をチェックして「メール本文が同じ内容だったっら削除」という事が条件になります。 要するに HTMLメールですね、コレは。 そう言うプログラムを作れば可能ですが、今回は Attachment フォルダの整理が目的なので除外しておきます。
また、EdMax の Attachment フォルダの整理が目的なので、フォルダ階層も1階層のみとします。 フォルダの中にフォルダがあって… という構成には対応しません。 具体的な階層イメージは、次のようになります。
EdMax └ Attachment ├ 20020217_015714_anzye0 ├ 20020217_015714_e0z5x2 │ : │ : ├ 20050512_173011_zm2xch └ 20050512_190954_h28jut
MS-DOS では、不特定多数のファイルを処理するには FOR
コマンドを使います。
例えばコマンドプロンプトで次のコマンドを実行すると、カレントディレクトリにあるファイル名の一覧が得られます。
セット(*.*
)に該当するファイル名を %f
に順次適用して、DO
以下のコマンドを繰り返し実行します。
この例では「%f
の内容を echo
する」という作業を *.*
に適合するファイル数だけ繰り返しているわけですね。
for %f in ( *.* ) do @echo %f
やってみれば分かりますが、検索対象は「ファイル」です。
上の例では、サブフォルダは対象外であり、一覧には出てきません。
今回は「フォルダ」を対象とする必要があるので、FOR
のコマンド拡張を使い /D
スイッチを付ます。
これで、セット内にワイルドカードがある場合にフォルダを検索する様になります。
上の例に /d
を追加したものですが、こんどはフォルダの一覧が得られます(逆にファイルの一覧は出てきません)。
for /d %f in ( *.* ) do @echo %f
あとは、DO
以下のコマンドに「指定されたフォルダ内の不要ファイルを削除した上でファイルの有無をチェックして、空フォルダだったら当該フォルダを削除」という処理を書いてあげます。
こんな長ったらしい処理が1コマンドで書けるわけないので、この内容もバッチファイルになります。
バッチファイルからバッチファイルを呼ぶには、CALL
コマンドを使います。
引数は、バッチファイル名かラベル。
バッチファイルがポコポコ出来ても面倒なので、ここはラベルを使うことにしましょうか。
バッチファイルは、ファイルの終端に達すると処理を終了します。
CALL
で呼ばれたバッチ処理も同様で、終了後は呼び元の次のコマンドに制御が返ります。
BASIC言語の GOSUB〜RETURN
みたいな感じです。
return させるにはファイル終端へ達する必要があるので、GOTO
でジャンプします。
たとえば :EndOfFile
なんてラベルをバッチファイル終端に用意して、このラベルに GOTO すればいいわけです。
が、ここもコマンド拡張が使え、:EOF
というキーワードでファイル終端へ制御を移すことができます。
フォルダ名の一覧とフォルダごとのファイル名の一覧を得るバッチファイルは、次のようになります。 各一覧を得る部分がサブルーチンになっています。
sample code
@echo off :BatchMain echo Folder List for /d %%d in ( *.* ) do call :ShowFolderName %%d echo/ echo File List for /d %%d in ( *.* ) do call :ShowFolderFiles "%%d" goto :EOF :ShowFolderName echo %1 goto :EOF :ShowFolderFiles echo ^<%~1^> for %%f in ( *.* ) do echo %%f goto :EOF
サイズが 0Byte のファイルを削除するには、ファイルのサイズを得る必要が有ります。
実は、変数参照の置換も拡張されています。
%~zI
構文を使うと、I
がファイルのサイズに展開されます。
I
は、フォルダ名(FOR /D
で取得)と ファイル名( FOR
で取得)を "\" で繋げば得られます。
引数 %1
でファイル名を受け取り、サイズが 0 でなければそのまま return 。
サイズが 0 であれば当該ファイルを削除して return という処理にすればよいでしょう。
sample code
:delempty if not %~z1==0 goto :EOF echo delete empty file : %~1 del %1 goto :EOF
空フォルダを削除するには、指定されたフォルダの中にファイルがあるか否かを判断すればよい事になります。
前提条件としてフォルダ中にサブフォルダは存在しないので、ここでは無視します。
フォルダ中のファイルについては、FOR
で得ることができます。
あとは判断の方法でしょう。
ここでは、環境変数を使います。
チェック用の環境変数、たとえば list
を用意して、見つかったファイル名を足して行きます。
FOR
が終わった時に環境変数 list
の内容が空であればファイルがない、すなわち空フォルダと判断出来ます。
sample code
:chkfolder set list= for %%f in ( %1\*.* ) do set list=%list% %%f if "%list%"=="" rmdir %1 goto :EOF
一見良さそうに見えますが、実は list
に入るのは最後に見つかったファイル名のみです。
ファイルの一覧を作りたかったのに。
これでも目的は達成できるのですが、思ったとおりに動かないのは問題です。
これは環境変数の展開が FOR
の開始時に1度だけ行われる事に原因があります。
コマンド拡張では遅延環境変数の展開がサポートされており、set list=!list! %%f
のように記述すればファイルの一覧が得られます。
ただし、コマンドインタープリタの /V:ON
スイッチで遅延環境展開を有効にしなければなりません。
面倒なので、ここもサブルーチンにしてしまいます。
環境変数 list
の展開が FOR
コマンドの外で行われるため、この様な問題は発生しません。
sample code
:chkfolder set list= for %%f in ( %1\*.* ) do call :addname %%f if "%list%"=="" rmdir %1 goto :EOF :addname set list=%list% %1 goto :EOF
以上をふまえ作成した、空フォルダを削除するバッチファイルです。
上記以外で注意すべき点として、添付ファイル名は空白を含んでいる可能性があるという点くらいです。
ファイル名は二重引用符(")で囲む必要が有ります。
引用符を除去するには、変数参照の %~I
構文を用います。
コマンド拡張を使えば、MS-DOS 時代にはプログラムを作るしかなかった事が幾らか出来るようになります。 けっこう拡張されているので、色々遊べるかも知れませんね。
cleaning.bat
@echo off rem ----------------------------------- rem メイン処理 rem ----------------------------------- for /D %%d in ( 2* ) do call :deluseless %%d for /D %%d in ( 2* ) do call :chkfolder %%d goto :EOF rem ----------------------------------- rem 不要ファイルを削除 rem ----------------------------------- :deluseless for %%f in ( "%1\*.*" ) do call :delempty "%%f" for %%f in ( "%1\Norton AntiVirus が削除しました*.txt" ) do call :delfile "%%f" goto :EOF rem ----------------------------------- rem 空ファイルを削除 rem ----------------------------------- :delempty if not %~z1==0 goto :EOF echo delete empty file : %~1 del %1 goto :EOF rem ----------------------------------- rem ファイルを削除 rem ----------------------------------- :delfile echo delete file : %~1 del %1 goto :EOF rem ----------------------------------- rem 各フォルダをチェック rem ----------------------------------- :chkfolder rem 指定フォルダ内のファイル名一覧を得る set list= for %%f in ( %1\*.* ) do call :addname "%%f" rem ファイルが存在しないならフォルダを削除 if "%list%"=="" call :delfolder %1 goto :EOF rem ----------------------------------- rem 環境変数 list にファイル名を追加 rem ----------------------------------- :addname if "%list%"=="" goto first :later set list=%list% %~1 goto :EOF :first set list=%~1 goto :EOF rem ----------------------------------- rem フォルダを削除 rem ----------------------------------- :delfolder echo delete folder : %~1 rmdir %1 goto :EOF