【ExcelVBA】サブフォルダ含めて全てのフォルダ名とファイル名を取得してシートに書き出すには

この記事では、サブフォルダ含めて全てのフォルダ名とファイル名を取得してシートに書き出す方法についてご説明します。

【動画】サブフォルダ含めて全てのフォルダ名とファイル名を取得してシートに書き出す実際の動き

本題に入る前に、まずは次のツイートをご覧ください。

シート「TOP」のB2セルに入手されたパスに対して、サブフォルダ含めて全てのフォルダ名とファイル名を取得してシート「data1」「data2」に書き出しています。
※シートの行数を超えるフォルダ名とファイル名が存在する場合は、都度シートを追加してフォルダ名とファイル名を書き出しています。

サブフォルダ含めて全てのフォルダ名とファイル名を取得してシートに書き出す方法

サブフォルダ含めて全てのフォルダ名とファイル名を取得してシートに書き出すには、次の流れの通りにコードを書いていきます。

Excelのマクロ

STEP.1
コマンドプロンプトを呼び出してサブフォルダ含めて全てのフォルダ名とファイル名を取得したいディレクトリをCSVファイルに書き出す
コマンドプロンプトを呼び出してサブフォルダ含めて全てのフォルダ名とファイル名を取得したいディレクトリをCSVファイルに書き出します。
STEP.2
フォルダ名・ファイル名を列挙するためのシートを新規作成
フォルダ名・ファイル名を列挙するためのシートを新規作成します。
STEP.3
STEP.1のCSVファイルを読み込む
STEP.1のCSVファイルを読み込みます。
STEP.4
読み込んだCSVファイルから、パスとフォルダ・ファイル名をセルに書き込む
読み込んだCSVファイルから、パスとフォルダ・ファイル名をセルに書き込みます。
なお、書き出せるのは1行ずつです。(1度にまとめて複数行(または全行)書き出すことはできません)
STEP.5
読み込んだCSVファイルの最後の行までSTEP.4を繰り返す
読み込んだCSVファイルの最後の行までSTEP.4を繰り返します。

コードの例

Excelのマクロのコード(例)

    Dim buf                 As String           '一時的な値の格納先変数
    Dim wkAry()             As String           '一時的な値の格納先配列
    Dim getPath             As String           'サブフォルダ含めて全てのフォルダ名とファイル名を取得したいパス
    Dim cmdTxt              As String           'コマンドプロンプトのコード用変数
    Dim cnt                 As Integer          'カウンタ(作業用)
    Dim shtNMCnt            As Integer          'シート名用カウンタ
    Dim rPos_cnt            As Long             'シートの行位置用カウンタ
    Dim ws                  As Worksheet        'Worksheet用変数
    Dim fldrFileNMExptTxt   As String           'フォルダ名とファイル名を出力させるCSVファイル名
    Dim st                  As ADODB.Stream     'バイナリ データまたはテキストのストリームのインスタンス用変数
    Dim wshObj              As WshShell         'WshShellオブジェクト
    Dim fso                 As FileSystemObject 'FileSystemObjectのインスタンス用変数

    'Excelの行数
    Const rowMAXNum     As Long = 1048576
    
    'フォルダ名とファイル名を出力させるCSVファイル名を取得する
    fldrFileNMExptTxt = data.csv"

    'シートの行位置用カウンタの初期値を設定
    rPos_cnt = 2
    
    'シート名用カウンタの初期値を設定
    shtNMCnt = 0
        
    'サブフォルダ含めて全てのフォルダ名・ファイル名を取得したいパス
    getPath = "c:\work"
    
    'バイナリ データまたはテキストのストリームのインスタンスを生成する
    Set st = New ADODB.Stream
    
    'FileSystemObjectのインスタンスを生成する
    Set fso = New FileSystemObject
    
    'WshShellオブジェクトからインスタンスを生成する
    Set wshObj = New WshShell
        
    If Right(getPath, 1) <> "\" Then
    
        '入力されたパスの末尾に「\」が付いていない場合に付ける
        getPath = getPath & "\"
        
    End If
    
    '実行するコマンド:dirコマンドでフルパスをCSVファイルに書き出す
    cmdTxt = "chcp 65001 | dir /b /s " & """" & getPath & """" & " > " & ActiveWorkbook.Path & "\" & fldrFileNMExptTxt & ""
    
    'コマンドプロンプトで変数「cmdTxt」のコマンドを実行する
    Call wshObj.Run("%ComSpec% /c " & cmdTxt, 1, WaitOnReturn:=True)
        
    'フォルダ名とファイル名貼り付け用シート有無のチェック
    For Each ws In Worksheets
        If ws.Name = "data" & CStr(shtNMCnt + 1) Then
            
            'フォルダ名とファイル名貼り付け用シートが存在する場合
            
            '確認ダイアログを非表示にする
            Application.DisplayAlerts = False
            
            'フォルダ名とファイル名貼り付け用シートを選択する
            Sheets("data" & CStr(shtNMCnt + 1)).Select
            
            'フォルダ名とファイル名貼り付け用シートを削除する
            ActiveWindow.SelectedSheets.Delete
            
            '確認ダイアログを表示可にする
            Application.DisplayAlerts = True
            
            shtNMCnt = shtNMCnt + 1
            
        End If
        
    Next ws
    
    'シート名用カウンタを0に初期化する
    shtNMCnt = 0
    
    'フォルダ名・ファイル名を列挙するためのシートを新規作成する
    Worksheets.Add(After:=Worksheets(Worksheets.Count)) _
                   .Name = "data" & CStr(shtNMCnt + 1)
    
    With Sheets("data" & CStr(shtNMCnt + 1))
    
        '1行目に項目を設定する
        .Range("A1").Value = "パス"
        .Range("B1").Value = "フォルダ名/ファイル名"
        
        'A列に色を付ける
        .Range("A1:B1").Interior.Color = RGB(252, 228, 214)
    
        'B列を「文字列」に設定する
        .Columns("B").NumberFormatLocal = "@"
    
    End With
    
    With st
    
        .Charset = "UTF-8"                              '文字セットにUTF-8を設定する
        .Open                                           'streamを開く
        .LoadFromFile ActiveWorkbook.Path & "\" & _
                      fldrFileNMExptTxt                 'フォルダ名とファイル名が出力されたファイルから読み込む
        
        'Streamの末尾まで繰り返す
        Do Until .EOS
        
            '取り出したテキストを変数「buf」に格納する
            buf = .ReadText(adReadLine)
                
            'フルパスから「\」を区切り文字としてフォルダ名・ファイル名を配列に格納する
            wkAry = Split(buf, "\")
            
            'フルパスからパスを切り出してA列に設定する
            Sheets("data" & CStr(shtNMCnt + 1)).Cells(rPos_cnt, 1).Value = Left(buf, Len(buf) - Len(wkAry(UBound(wkAry))))
            
            'フルパスからフォルダ名またはファイル名を切り出してB列に設定する
            Sheets("data" & CStr(shtNMCnt + 1)).Cells(rPos_cnt, 2).Value = wkAry(UBound(wkAry))
            
            'フルパスがフォルダなのかファイルなのかを判定する(True:ファイル/False:フォルダ)
            If fso.FileExists(buf) Then
                
                'ファイルの場合
            
                'B列のセルに色を付ける(※本マクロではファイルの場合は黄色を付けるようにしています)
                Sheets("data" & CStr(shtNMCnt + 1)).Cells(rPos_cnt, 2).Interior.Color = RGB(255, 255, 200)
                
            Else
            
                'フォルダの場合
            
                'B列のセルに色を付ける(※本マクロではフォルダの場合は緑色を付けるようにしています)
                Sheets("data" & CStr(shtNMCnt + 1)).Cells(rPos_cnt, 2).Interior.Color = RGB(198, 224, 180)
            
            End If
            
            rPos_cnt = rPos_cnt + 1
            
            'Excelの行数を超える場合
            If (rPos_cnt Mod rowMAXNum) = 0 Then
            
                shtNMCnt = shtNMCnt + 1

                '(Excelの行数を超えるので)フォルダ名・ファイル名を列挙するためのシートを新規作成する
                Worksheets.Add(After:=Worksheets(Worksheets.Count)) _
                               .Name = "data" & CStr(shtNMCnt + 1)
                
                With Sheets("data" & CStr(shtNMCnt + 1))
                
                    '1行目に項目を設定する
                    .Range("A1").Value = "パス"
                    .Range("B1").Value = "フォルダ名/ファイル名"
        
                    'A1とB1のセルに色を付ける
                    .Range("A1:B1").Interior.Color = RGB(252, 228, 214)
                
                    'B列を「文字列」に設定する
                    .Columns("B").NumberFormatLocal = "@"
                
                End With

                'シートの行位置用カウンタの初期値を設定(シートを追加したのでまた2行目からカウントする)
                rPos_cnt = 2
                
            End If
            
            DoEvents
            
        Loop
        
        'Streamの末尾まで処理が終わったので、streamを閉じる
        .Close
        
    End With
    
    For cnt = 0 To shtNMCnt
    
        With Sheets("data" & CStr(cnt + 1))
    
            'セルの幅を自動調整する
            .Cells.EntireColumn.AutoFit
            
            If cnt = shtNMCnt Then
            
                'A列に色を付ける
                .Range("A2:A" & rPos_cnt - 1).Interior.Color = RGB(232, 236, 239)
            
                'セルに罫線を引く
                .Range("A1:B" & rPos_cnt - 1).Borders.LineStyle = xlContinuous
            
            Else
            
                'A列に色を付ける
                .Range("A2:A" & rowMAXNum - 1).Interior.Color = RGB(232, 236, 239)
            
                'セルに罫線を引く
                .Range("A1:B" & rowMAXNum - 1).Borders.LineStyle = xlContinuous                
            
            End If
            
        End With
    
    Next
    
    '後処理
    
    'フォルダ名とファイル名が出力されたファイルを削除する
    Call fso.DeleteFile(ActiveWorkbook.Path & "\" & fldrFileNMExptTxt, True)
    
    Set st = Nothing
    Set fso = Nothing

コードの解説

注目すべきコード①

最初に見て頂きたいのは46行目から49行目です。

    '実行するコマンド:dirコマンドでフルパスをCSVファイルに書き出す
    cmdTxt = "chcp 65001 | dir /b /s " & """" & getPath & """" & " > " & ActiveWorkbook.Path & "\" & fldrFileNMExptTxt
    
    'コマンドプロンプトで変数「cmdTxt」のコマンドを実行する
    Call wshObj.Run("%ComSpec% /c " & cmdTxt, 1, WaitOnReturn:=True)

46行目でコマンドプロンプトでdirコマンドを実行するためのコマンド文を変数に格納し、そのコマンド文を49行目でRunメソッドの引数に設定してコマンドプロンプトでdirコマンドを実行しています。

なぜわざわざコマンドプロンプトを使っているのか(呼び出しているのか)というと、処理の高速化を図るためです。

大量のフォルダやファイルが存在する場合に、マクロだけでやろうとすると時間がかかりすぎるために高速化が実現できるコマンドプロンプトを使っています。
(※絶対にコマンドプロンプトを使わなければいけないというわけではありませんが、今回は手軽に使うことができるコマンドプロンプトを採用しました)

例えば、私のパソコンのCドライブ配下にはサブフォルダ含めて全てのフォルダとファイルの数はファイル数が110万超、フォルダー数が250万近くありますが、これほどの数をExcelマクロだけでシートに書き出そうとすると何時間もかかってしまいます。

これだけの大量にあるフォルダとファイルを取得してシートに書き出したい場合に、マクロだけで行うのは処理時間を考えると現実的ではないです。

一方、コマンドプロンプトを呼び出してdirコマンドを使い(Cドライブ配下の)、ファイル名とサブディレクトリ名を一覧出力すると(私のパソコンでは)20分弱で終わりました。(完了するまでの時間はPCのスペックに左右されますが)

話を戻しますが、46行のコードではどんなコマンド文が呼び出されるのか分かりにくいのでサンプルをお見せします。

chcp 65001 | dir /b /s "C:\" > C:\work\data.csv

このコマンド文は1行ですが、実は2つのコマンドを1行で実行しています。

2つのコマンドは次の通りです。
①chcp 65001
②dir /b /s “C:\” > C:\work\data.csv

コマンドの説明
  1. ①のコマンドは、文字コードをUTF-8に設定するコマンドで、文字化けしないように文字コードをUTF-8に設定しています。
    (文字コードをUTF-8に設定すると文字化けしない)
  2. ②のコマンドは、dirコマンドを実行してファイル「data.csv」にディレクトリにあるファイルとサブディレクトリの一覧出力しています。
コマンドの「|」とは?
コマンドで使われている「|」の文字は、実行する複数のコマンドを1行で記述したい時に使う文字です。

以上、上記のコマンドが実行されると、フォルダ名とファイル名が出力された「data.csv」というファイルが出力されます。

この「data.csv」をExcelマクロが読み込んでシートにフォルダ名とファイル名をセルに書き出していきます。

ちなみに、フォルダ名とファイル名を全てセルに書き出したら、必要がなくなるので206行目で削除します。

    'フォルダ名とファイル名が出力されたファイルを削除する
    Call fso.DeleteFile(ActiveWorkbook.Path & "\" & fldrFileNMExptTxt, True)
注目すべきコード②

次に見て頂きたいのは81行目から103行目です。

        With st
    
        .Charset = "UTF-8"                              '文字セットにUTF-8を設定する
        .Open                                           'streamを開く
        .LoadFromFile ActiveWorkbook.Path & "\" & _
                      fldrFileNMExptTxt                 'フォルダ名とファイル名が出力されたファイルから読み込む
        
        'Streamの末尾まで繰り返す
        Do Until .EOS
        
            '取り出したテキストを変数「buf」に格納する
            buf = .ReadText(adReadLine)
                
            'フルパスから「\」を区切り文字としてフォルダ名・ファイル名を配列に格納する
            wkAry = Split(buf, "\")
            
            'フルパスからパスを切り出してA列に設定する
            Sheets("data" & CStr(shtNMCnt + 1)).Cells(rPos_cnt, 1).Value = Left(buf, Len(buf) - Len(wkAry(UBound(wkAry))))
            
            'フルパスからフォルダ名またはファイル名を切り出してB列に設定する
            Sheets("data" & CStr(shtNMCnt + 1)).Cells(rPos_cnt, 2).Value = wkAry(UBound(wkAry))

83行目で、文字コードをUTF-8に設定してフォルダ名とファイル名が出力された「data.csv」をExcelマクロが読み込むためにCharsetプロパティに「UTF-8」の文字列を設定しています。

次に84行目でOpenメソッドを実行し、85行目のLoadFromFile メソッドの引数に「data.csv」を設定して「data.csv」を読み込みます。

89行目のDoループで、読み込んだ「data.csv」の行数分処理を繰り返します。

92行目では、1行取り出して変数「buf」に格納しています。この1行はフルパスになります。

取り出した1行(フルパス)から「\」を区切り文字としてフォルダ名・ファイル名を配列「wkAry」に格納し、フルパスからパスとフォルダ名またはファイル名を取り出します。

パスは98行目でA列のセルに、フォルダ名またはファイル名は101行目でB列のセルに設定します。

注目すべきコード③

次に見て頂きたいのは138行目から144行目です。

            'Excelの行数を超える場合
            If (rPos_cnt Mod rowMAXNum) = 0 Then
            
                shtNMCnt = shtNMCnt + 1
 
                '(Excelの行数を超えるので)フォルダ名・ファイル名を列挙するためのシートを新規作成する
                Worksheets.Add(After:=Worksheets(Worksheets.Count)) _
                               .Name = "data" & CStr(shtNMCnt + 1)

フォルダとファイルの数がExcelの最大行数を超える場合は、新たにシートを追加します。

最大行数を超えるかを138行目で判定し、もし超える場合は143行目で新たにシートを作成します。

注目すべきコード④

次に見て頂きたいのは27行目です。

    'サブフォルダ含めて全てのフォルダ名・ファイル名を取得したいパス
    getPath = "c:\work"

27行目で、サブフォルダ含めて全てのフォルダ名とファイル名を取得するパスを指定しています。

サブフォルダ含めて全てのフォルダ名とファイル名を取得してシートに書き出した結果

本マクロを実行すると、サブフォルダ含めて全てのフォルダ名とファイル名を取得してシートに書き出されます。

【注意】参照設定が必要です

一つ注意点があるのですが、先ほどのコードを動かすには参照設定が必要です。

参照設定の一覧(下の画像を参考)から次の項目(ライブラリ)にチェックを付けて「OK」ボタンをクリックします。

  1. Microsoft ActiveX Data Objects 2.8 Library(msado28.tlb)
  2. Windows Script Host Object Model(wshom.ocx)

なぜ必要かというと、先ほどのコードの10行目の「ADODB.Stream」というオブジェクトが「msado28.tlb」というファイルを、11行目の「WshShell」と12行目の「FileSystemObject」というオブジェクトが「wshom.ocx」というファイルを参照するからです。

    Dim st                  As ADODB.Stream     'バイナリ データまたはテキストのストリームのインスタンス用変数
    Dim wshObj              As WshShell         'WshShellオブジェクト
    Dim fso                 As FileSystemObject 'FileSystemObjectのインスタンス用変数

この参照設定をしないと下の画像のエラーが出ますので必ず行う必要があります。

ここでは「msado28.tlb」と「wshom.ocx」とは何者かについては記事の本題から逸れてしまうので詳細は割愛しますが、マクロで「ADODB.Stream」「WshShell」「FileSystemObject」というオブジェクトを使う場合は参照設定しないと動かない、程度に思って頂ければと思います。

最後に

本記事では、サブフォルダ含めて全てのフォルダ名とファイル名を取得してシートに書き出す方法についてご説明しました。

サブフォルダ含めて全てのフォルダ名とファイル名を取得したい場合は参考にしてみてくださいね。

プログラミングのスキルを習得するなら

プログラミングのスキルを習得したい、今のスキルをもっと高めたい、そう考えているなら「プログラミングスクール」がおすすめです。

プログラミングのスキルの基礎を身につけるなら「TechAcademy」で1週間の無料体験があるので、これで「プログラミングの基礎」を学ぶのにおすすめですよ。

→ TechAcademyの「1週間 無料体験」はこちら