[Windows]batとvbsでzip化する!(ネットワーク越しのディレクトリコピーもするよ)

bat vbs zip dir copy

 

 

こんにちは。ナガオカ(@boot_kt)です。

 


Windowsの標準の機能でzip化しようとすると右クリックしかありません

zip化するdosコマンドは無いため、batでzip化することはできません

コマンドラインでzip化するためには7zip等、コマンドラインのインタフェースを準備してくれているソフトをインストールする必要があります

 

しかしっ!!

ソフトをインストールできない場合もあるでしょう!!

 

この記事ではソフトをインストールせずにzip化するbatを紹介します!

この記事のザックリした内容
◆対象読者

  • コマンド(bat/vbs)でzip化したい
  • ソフトをインストールせず、Windows標準の機能だけで実現したい
  • batでサーバーからローカルへコピーしてzip化もしたい

 

◆解決できるかも知れないお悩み

  • ソフトをインストールしなくても、bat(vbs)でzip化したい

 

 

 

 

 

Windowsにはzip化するコマンドは無いの?

 

無いです!

 

右クリックでzip化機能を提供してるのに、、、

 

なんでbatで使えるコマンドが無いんだあーーーー!!!!

 

と思って探しまくったけど、やっぱり無いんですよ

 

 

で、ググりまくると、、、、

 

VBスクリプトでできるよ

 

という情報があった!!!!

 

 

『VBスクリプトでzip化する』という視点でググりまくりました!!

 

zip化を実現してくれるVBスクリプトを教えてくださっている記事は何本かありました(ありがとうございます!)

 

何本か試してみたところ、確かにzip化してくれて僕が思ってるようなことはできるんだけど・・・・

理由は分からないんだけど、zip化してくれないファイルがあったんだよね

今回zip化したい目的はディレクトリ全体をバックアップしたいので、zip化してくれないファイルがあるのはちょいまずいなぁと・・・・

 

で、色々試した結果、この記事で紹介するVBスクリプトに辿り着きました

もしかしたら他にもzip化してくれないファイルがあるかも知れないけど、ひとまずはこれで。

 

紹介するbatとvbsの簡単な説明

やりたいこと

  1. サーバーにあるディレクトリ(0個以上)をローカルへバックアップ目的でコピーする
  2. コピーしたディレクトリはzip化して、zipのファイル名に日付と時刻を付与する
  3. この処理はタスクスケジューラに登録して定期的に動かす

 

プログラム構成

  • BkupMain.bat
    1. サーバーのディレクトリをネットワークドライブとして接続する
    2. ネットワークドライブからローカルへコピーをする
    3. MakeZip.vbsを呼び出す
  • MakeZip.vbs
    1. 圧縮処理をする
    2. 圧縮処理が終了したかどうかを3秒毎に判断する
    3. 圧縮処理が終了していないなら処理続行
    4. 圧縮処理が終了したのならプログラム終了

 

batプログラムとvbsプログラム

では、プログラムを以下に貼り付けておきます

GitHubにも置いてます
dir_copy_and_zip | GitHub

 

bat(BkupMain.bat)

@echo off

REM **************************************************
REM サーバーからローカルへコピーしてzipにするbat
REM
REM カスタマイズするなら「★」マークの部分を変更するべし
REM **************************************************

cd /d %~dp0

REM 遅延環境変数の有効化
setlocal ENABLEDELAYEDEXPANSION


REM **************************************************
REM
REM 変数及び定数
REM
REM **************************************************
REM ==================================================
REM 本日日付と時刻
REM ==================================================
set TODAY_STR=%date:~2,2%%date:~5,2%%date:~8,2%
set time_temp=%time: =0%
set TIME_STR=%time_temp:~0,2%%time_temp:~3,2%%time_temp:~6,2%

REM ==================================================
REM ★擬似的な配列
REM %SERVER_UNC%の中にあるフォルダ名を書く
REM
REM アンダーバーの後ろの数字は配列の添字と思ってください
REM batに配列は無いので、擬似的に配列っぽいものを実現している
REM
REM 擬似的なFOREACHで処理しているので、気にせず増減してOK
REM 但し要素番号は連続したものにしてね
REM ==================================================
set FOLDER[0]=folder0
set FOLDER[1]=folder1
set FOLDER[2]=folder2

REM 事前チェック
call set it=%%FOLDER[0]%%
if not defined it (
	echo バックアップ対象が設定されていないため終了します
	exit /b 0
)


REM ==================================================
REM ★ワーキングディレクトリ
REM ・サーバーからコピーするフォルダを置くところ
REM ・zipファイルを置くところ
REM
REM サンプル→ C:\fuga\hoge\
REM ==================================================
rem ★work_folderの最後には\マークを付けない
set work_folder=work_fol
rem ★WORKING_DIRの最後には\マークを付ける
set WORKING_DIR=%~dp0%work_folder%\%TODAY_STR%\
echo %WORKING_DIR%

if not exist %WORKING_DIR% (
	echo ワーキングディレクトリは存在しないので作成します
	mkdir %WORKING_DIR%
) else (
	REM どこに接続されているかわからないので一旦切断する
	echo ワーキングディレクトリは存在する
)


REM ==================================================
REM ネットワークドライブ関係の変数
REM Xドライブ
REM ==================================================
REM ★ドライブレター
set NETWORKDRIVE=x:
REM ★サーバー
set SERVER_UNC=\\servername\hoge\fuga

set NET_USE_CMD=net use %NETWORKDRIVE%
set NETWORKDRIVE_CREATE=%NET_USE_CMD% %SERVER_UNC%
set NETWORKDRIVE_DELETE=%NET_USE_CMD% /delete /y


REM ==================================================
REM ★VBSプログラム(zipにしてくれるやつ)
REM ==================================================
set VBS_FILE=cscript MakeZip.vbs


REM ==================================================
REM ★タイムアウト
REM ==================================================
set TIMEOUT_SEC=5




REM **************************************************
REM
REM 本処理
REM
REM **************************************************
REM ==================================================
REM ネットワークドライブが存在していたら切断する
REM ==================================================
if not exist %NETWORKDRIVE% (
	echo ネットワークドライブは存在しない
) else (
	REM どこに接続されているか分からないので一旦切断する
	echo ネットワークドライブは存在するので一旦強制的に切断します
	%NETWORKDRIVE_DELETE%
)


REM ==================================================
REM ネットワークドライブ接続
REM ==================================================
echo ネットワークドライブを接続します
%NETWORKDRIVE_CREATE%


REM ==================================================
REM バックアップ処理をする
REM ==================================================
REM FOLDER配列っぽいものででFOREACHっぽい処理をする
REM ※batにはFOREACHの命令は無いので擬似的なもの

REM --------------------------------------------------
REM サーバーからローカルへコピー
REM --------------------------------------------------
set i=0
:FOREACH_COPY
call set it=%%FOLDER[!i!]%%
if defined it (
	echo %NETWORKDRIVE%\!it!

	REM サーバーからローカルへコピー
	set XCOPY_CMD=xcopy /s /e /i /h /k /q %NETWORKDRIVE%\!it!  %WORKING_DIR%!it!
	echo %XCOPY_CMD%
	call %XCOPY_CMD%

	REM 次の要素へ移動
	set /A i+=1
	goto :FOREACH_COPY
)


REM --------------------------------------------------
REM コピーの終了を検知することができないので、数秒待つことにする
REM ※コピー元/先のサイズを比較すれば終了が分かる思うが、
REM   面倒なので数秒待つだけにする
REM --------------------------------------------------
timeout /t %TIMEOUT_SEC% /nobreak


REM --------------------------------------------------
REM ZIP化
REM --------------------------------------------------
set i=0
:FOREACH_ZIP
call set it=%%FOLDER[!i!]%%
if defined it (
	echo !it!

	REM vbsでzipにする
	set PARAM1=%WORKING_DIR%!it!
	set PARAM2=!PARAM1!_%TODAY_STR%_%TIME_STR%.zip
	set ZIP_CMD=%VBS_FILE%  !PARAM1!  !PARAM2!
	echo %ZIP_CMD%
	call %ZIP_CMD%

	REM 次の要素へ移動
	set /A i+=1
	goto :FOREACH_ZIP
)


REM ==================================================
REM ネットワークドライブ切断
REM ==================================================
%NETWORKDRIVE_DELETE%


REM ==================================================
REM 後始末処理
REM ==================================================
REM --------------------------------------------------
REM ローカルにコピーしたフォルダを削除する
REM --------------------------------------------------
set i=0
:FOREACH_RMDIR
call set it=%%FOLDER[!i!]%%
if defined it (
	echo !it!

	REM ローカルにコピーしたフォルダを削除する
	set PARAM1=%WORKING_DIR%!it!
	set RMDIR_CMD=rmdir /s /q  !PARAM1!
	echo %RMDIR_CMD%
	call %RMDIR_CMD%

	REM 次の要素へ移動
	set /A i+=1
	goto :FOREACH_RMDIR
)


REM ==================================================
REM 終了
REM ==================================================
exit /b 0

 

vbs(MakeZip.vbs)

'**************************************************
' ディレクトリをzipファイルにする
'
' ◆期待する引数
'   1個目:対象ディレクトリ(フルパスで)
'          サンプル→ C:\hoge\fuga
'   2個目:zipファイル名(フルパスで)
'          サンプル→ C:\hoge\fuga.zip
' ◆注意
'   引数のディレクトリ存在チェックはやってないよ
'
' ◆実行方法
'   > cscript MakeZip.vbs  param1  param2
'
' ◆!!重要!!
'   システムファイル等を圧縮してくれないファイルがあるかも知れない
'   使用に際してはよく確認すること
'
' カスタマイズするなら「★」マークの部分を変更するべし
'**************************************************
Option Explicit


'**************************************************
'
' ★定数
'
'**************************************************
Dim COMP_END_WAIT_MSEC
COMP_END_WAIT_MSEC = 3000


'**************************************************
'
' 変数
'
'**************************************************
Dim str
Dim Shell
Dim Fso
Dim WshShell
Dim objFolder
Dim Handle
Dim EmptyData
Dim strTargetZipFile
Dim objTargetFolder
Dim SINK
Dim objWMIService
Dim sv_counter
Dim counter
Dim ZipFile


'--------------------------------------------------
' cscriptで実行しているかの確認
'--------------------------------------------------
str = WScript.FullName
str = Right(str, 11)
str = Ucase(str)

if str <> "CSCRIPT.EXE" then
	Wscript.Echo "cscript.exeで実行してください"
	Wscript.Quit
end if


'--------------------------------------------------
' 引数があるかどうかを確認
'--------------------------------------------------
if WScript.Arguments.Count <> 2 then
	WScript.echo("error : Argument counts are invalid.")
	WScript.Quit(-1)
end if

' 引数を取得
objFolder = WScript.Arguments(0)
ZipFile   = WScript.Arguments(1)


'**************************************************
'
' 本処理
'
'**************************************************
'--------------------------------------------------
' 必要な基本オブジェクト
'--------------------------------------------------
Set Shell    = WScript.CreateObject("Shell.Application")
Set Fso      = WScript.CreateObject("Scripting.FileSystemObject")
Set WshShell = WScript.CreateObject("WScript.Shell")


'--------------------------------------------------
' 空のzip書庫作成
'--------------------------------------------------
Set Handle = Fso.CreateTextFile(ZipFile, True)
EmptyData = Chr(&H50) & Chr(&H4B) & Chr(&H5) & Chr(&H6)
EmptyData = EmptyData & String(18, Chr(0))

Handle.Write EmptyData
Handle.Close


'--------------------------------------------------
' 圧縮
'--------------------------------------------------
Set objTargetFolder = Shell.NameSpace(ZipFile)


'--------------------------------------------------
' 非同期の圧縮処理
'--------------------------------------------------
Call objTargetFolder.CopyHere(objFolder, 0)


'--------------------------------------------------
' WMI非同期イベントの監視
'--------------------------------------------------
Set SINK = WScript.CreateObject("WbemScripting.SWbemSink", "SINK_")
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
objWMIService.ExecNotificationQueryAsync  SINK, _
                                          "SELECT "                                      & _
                                          "  *    "                                      & _
                                          "FROM   "                                      & _
                                          "  __InstanceOperationEvent WITHIN 1 "         & _
                                          "WHERE "                                       & _
                                          "  TargetInstance ISA 'Win32_Process' "        & _
                                          "  AND TargetInstance.Name = 'explorer.exe' "

Wscript.Echo "ZIP保存処理を開始します(=> 終了するまで何も触らないでください)"


'--------------------------------------------------
' 圧縮処理が終了したか3秒毎に判断する
'--------------------------------------------------
Do
	sv_counter = counter
	WScript.Sleep COMP_END_WAIT_MSEC
	if sv_counter = counter then
		Wscript.Echo "処理を終了します"
		Wscript.Quit
	end if
Loop


'==================================================
' explorer.exeの動作中に呼び出される
'==================================================
Sub SINK_OnObjectReady(objLatestEvent, objAsyncContext)
	counter = counter + 1
	Wscript.Echo "... 処理中:" & objLatestEvent.TargetInstance.Name
End Sub

応用したい方に…

batとvbs、2本のプログラムを管理するのって面倒だったりします

batの中でvbsファイルを生成して、使ったら削除して、みたいなことをすると、batの1本だけを管理すればオッケーみたいなこともできます

 

↓以前僕が作ったプログラムでは、batの中でTeraTermマクロを生成するということをやっています

batとTeraTermマクロの組み合わせ | GitHub

 

別にこのやり方が良いってワケじゃなく、こういうやり方もありますよというだけの話です

 

広告




 

さいごに、

  • ソフトをインストールするな
  • インターネットにつなげるな
  • SubversionとかRedmine等ツールを使うな
  • Excelファイルで全て管理せよ!!!

と、まぁ、人力手作業を大好きな会社の是非は置いときますが、

 

個人的には属人要素(人力、手作業、記憶力、チェックシート、気を付ける)に頼ってはいけないと考えています

 

間違えない or 間違いに気付くような仕組みが大切、ラクすればするほど質は上がると思っています

 

しかしソフトを入れるなみたいな制約が多い場合には、OSが持つ機能だけでなんとかする必要があります

今回のバックアップ用batとzip化用vbsはそんな制約の中で調べて作りました

 

Pythonとかインストールできればもっとラクに作れたとは思うんですけど、まぁ、苦肉の策です

 

広告




 

参考書籍

 


 

プログラミング のレッスンに興味がある方、レッスン内容を聞いてみたい方、なんなりとお問い合わせください。
無料体験レッスンもありますのでお気軽にどうぞ!!!

 

 

bat vbs zip dir copy

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

ABOUTこの記事をかいた人

Windows/Mac/Linuxを使う現役システムエンジニア&プログラマ。オープン系・組み込み系・制御系・Webシステム系と幅広い案件に携わる。C言語やC#やJava等数多くのコンパイラ言語を経験したが、少し飽きてきたので、最近はRubyやPython、WordPressなどのWeb系を修得中。初心者向けのプログラミング教室も運営中。オンライン・対面・出張等でプログラミングをレッスンします。