VBAの勉強エントリです。
自分がデータ分析とかをやる場面ではべつにVBAを使う必要はなく、RやPythonでやればいいのですが、会社の仕事で他の人たちと共同作業する上では、Excelとかのマクロが組めると便利だろうなと思うことが多いです。
しかしほっといたらいつまでたっても勉強しないので、「とりあえず使いこなせるレベルにはならなくていいから、VBAで何かやるときの作業イメージをつかんで、いざ必要になったときの心理的ハードルを下げておきたい」と思い、ちょっといじってみています。
何かVBAでできそうなタスクがあったときに、「ググりながら時間かければ俺でも何とかできるかも」という前向きなモチベーションを持てるようにしておくのが目的です。
フォルダの同期
VBAで2つのフォルダの中身を比較して、差分の同期をする方法を検索したら、robocopyというWindowsのコマンドをVBAから起動する方法が載っていた。
【robocopyコマンドでフォルダーをバックアップ/同期する】【エクセル2013,VBA】 - DuKiccoの雑記
Office TANAKA - Excel VBA Tips[MS-DOSコマンドの標準出力を取得する]
Tech TIPS:Windowsのrobocopyコマンドでフォルダーをバックアップ/同期させる - @IT
VBAの練習台にと思って、適当にボタンを配置して、同期元と同期先のフォルダをそれぞれ選択し、同期するマクロを作りました。
練習台なのでかなり適当です。
「同期元を選択」ボタンを押すとダイアログが開くので、フォルダを選択すると、E4セルに書きこまれます。「同期先を選択」はE9セルに書き込みます。
それで「同期!」ボタンを押すと差分同期が始まり、B15以下のセルに処理のログが書きこまれます。「結果の削除」ボタンを押すとログが消えます。

同期元フォルダを選択するボタン用のコード
前回のエントリでも使った「msoFileDialogFolderPicker」ってのを使い、GUIで対話的にフォルダを選択して、選択したフォルダのパスを所定のセルに記入するようにします。
Sub GetSrcPath()
Dim strSrcPath As String ' フォルダのパスを格納する変数
Dim dlgFolder As Office.FileDialog ' ダイアログを受け取る変数
Set dlgFolder = Application.FileDialog(msoFileDialogFolderPicker)
If dlgFolder.Show = False Then ' キャンセルが押されたら抜ける
Exit Sub
Else
strSrcPath = dlgFolder.SelectedItems(1) & "\" ' 選択されたフォルダのパスを受け取る
Range("E4").Value = strSrcPath ' セルに書き込む
End If
End Sub
同期先フォルダを選択するボタン用のコード
上とほぼ同じです。
Sub GetDstPath()
Dim strDstPath As String ' フォルダのパスを格納する変数
Dim dlgFolder As Office.FileDialog ' ダイアログを受け取る変数
Set dlgFolder = Application.FileDialog(msoFileDialogFolderPicker)
If dlgFolder.Show = False Then ' キャンセルが押されたら抜ける
Exit Sub
Else
strDstPath = dlgFolder.SelectedItems(1) & "\" ' 選択されたフォルダのパスを受け取る
Range("E9").Value = strDstPath ' セルに書き込む
End If
End Sub
フォルダを同期するボタン用のコード。
前提として、VBEの参照設定で「Windows Script Host Object Library」への参照をONにしておく必要があります。
WScript.Shellは、Execメソッドに対して文字列でWindowsのコマンドを与えてやればそれが実行されるようで、かなり便利ですね。
なおrobocopyは、MacやLinuxのrsyncっていうコマンドに似ていて、「robocopy src dst /option」という書き方で、srcからdstへのコピーを行ってくれるようです。/mirというオプションは、フォルダの中がばっちり同じ内容になるやつ。
コマンドの実行に時間がかかるので、VBA側では、処理が終了するまで待つためのループを書くらしいのですが、ふつうに書くとOSのコマンドに処理を投げたあとはほったらかしでVBA内の次の処理に行ってしまうということなんでしょうか。
処理のログは、1つのセルに書くと見づらいので、改行コードで分割して配列にし、ループで1行1行書いていくようにしました。
Sub RunRoboCopy()
Dim WSH As IWshRuntimeLibrary.WshShell
Dim wExec
Dim sCmd As String
Dim Result As String
Dim Src As String
Dim Dst As String
Dim Lines As Variant
Dim i As Integer
Src = Range("E4").Value '同期元フォルダのパス
Dst = Range("E9").Value '同期先フォルダのパス
Set WSH = CreateObject("WScript.Shell")
sCmd = "robocopy " & Src & " " & Dst & " /mir" '/mirオプションを付けたrobocopyコマンド
Set wExec = WSH.Exec("%ComSpec% /c " & sCmd) 'コマンドの実行
Do While wExec.Status = 0 '処理が終了するのを待つループ
DoEvents
Loop
Result = wExec.StdOut.ReadAll ' 結果の取得
Lines = Split(Result, vbCrLf) ' 改行コードで分割して配列を得る
For i = 0 To UBound(Lines) ' 1行ごとに出力するループ
Cells(i + 15, 2) = Lines(i)
Next i
Set wExec = Nothing
Set WSH = Nothing
End Sub
結果を削除するボタン用のコード
下端まで指定する方法が良く分からなかったので1万行目まで消すようにした。
Sub ClearResult()
Range(Cells(15, 2), Cells(10000, 2)).Clear
End Sub