[[20180419124335]] 『空フォルダの削除方法でより効率的な方法ないです』(すけろく) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『空フォルダの削除方法でより効率的な方法ないですか』(すけろく)

自社サーバー内のフォルダ整理をしています。
その過程の中で空フォルダがあれば削除をするのですが、
あまりに数が多く(全体で1億フォルダほど)、手作業では
無理と判断し、VBAでコードを組みました。

消すこと自体はなんとかできているのですが、For-NextやDo-Loopで
ぐるぐるまわしていることもあり、かなりの時間を要しています。

より効率的な(作業時間を短縮できるような)方法がありましたら
ご教示いただけますと助かります。

現在のおおまかなロジックは
1.基本となるフォルダを指定
2.再帰処理でフォルダ内にサブフォルダやファイルの無いフォルダを
 リスト化
3.2のリストを削除
4.2と3をリスト化できなくなるまで繰り返す
です。

現状組んでいるコードは下記になります。

 Dim Cnt As Long
 Dim FSO As Object
 Dim FolderList()

 Sub TEST()
    Dim i as Long
    Set FSO = CreateObject("Scripting.FileSystemObject")
    Do
        Call FolderSearch("C:\TEST(1)")
        If UBound(FolderList) = -1 Then Exit Do
        For i = 0 To UBound(FolderList)
            FSO.DeleteFolder FolderList(i)
        Next i
        FolderList = Array()
        Cnt = 0
    Loop
    Set FSO = Nothing
 End Sub

 Sub FolderSearch(myFolder)
    Dim mySubFolder
    With FSO.GetFolder(myFolder)
        If .SubFolders.Count > 0 Then
            For Each mySubFolder In .SubFolders
                If FSO.GetFolder(mySubFolder).SubFolders.Count = 0 And FSO.GetFolder(mySubFolder).Files.Count = 0 Then
                    ReDim Preserve FolderList(Cnt)
                    FolderList(Cnt) = mySubFolder
                    Cnt = Cnt + 1
                End If
                Call FolderSearch(mySubFolder)
            Next
        End If
    End With
 End Sub

< 使用 Excel:Excel2010、使用 OS:Windows7 >


http://mattintosh.hatenablog.com/entry/20150914/1442235675

FSOが遅いのは割と有名な話です。

Dir関数 または Dirコマンド
なとで検索しながら他の方の回答をお待ちください。

サブフォルダを探すのは検索して出来ると思うけど、
空かどうかの判定がちょっとあやふや^^;
ちらっと検索限りではズバリのサンプルは無さそうなので、
自分でロジックを考えないといけないけど、
ちょっと思いつかないので、この程度の情報ですみません。

(まっつわん) 2018/04/19(木) 13:19


横から失礼します。

空フォルダとは?

1.目に見えるファイルやサブフォルダが無い
2.隠しファイルや隠しフォルダも含まれてない
3.目に見えるファイルや隠しファイルが含まれていない(サブフォルダは含まれていても
  その中に目に見えるファイルや隠しファイルが含まれていない)
4.その他

3.の場合でしたら、FileSystemObjectでフォルダのSizeを取得し0であれば空
フォルダと判定できます。その時点で、そのフォルダ以下は調べなくてもOK、となります。
(カリーニン) 2018/04/19(木) 13:27


まっつわん様
情報ありがとうございます。参考にさせていただきます!
確かにFSOが遅い、というのは良く聞くお話ですね・・。
何か別のアプローチがあれば・・と模索しております。

カリーニン様
返信ありがとうございます。
空フォルダの定義についてですが、3となります。
なるほど。上位階層のフォルダサイズが0だと確かに削除対象ですね。
しかしながら、ファイル数が多かったり、階層が深かったりすれば時間が
かかってしまいます。
ただ単にフォルダサイズが0か否かというような判定ってできるんでしょうか。
(すけろく) 2018/04/19(木) 14:13


ちょっと忙しくてコード書く余裕が無いので考え方だけ。

まず、フォルダ一覧は、私がここで良く書いている DIR /B/S を利用したものを探し、パラメータを /B/S/A:D として、親フォルダ下の*.*を指定してみてください。 全部シートに吐くと量が多い場合、少し下の階層を指定して試すと良いでしょう。

後は、FSOでも使って、ファイル数フォルダ数サイズ等の情報を得て判定し、削除していけば良いでしょう。 ファイル含めてフォルダを消す、コマンドプロンプトの RMDIR /S/Q という手もあります。

問題は、サムネイルやExcelの排他情報等のシステムファイル属性を消せるかですね。普通に消そうとしても、システムファイル関係だからということで、OSに拒否されてしまうはずです。
(???) 2018/04/19(木) 16:53


https://forest.watch.impress.co.jp/library/software/glaryutils/
https://www.gigafree.net/system/clean/emptyfoldelmanager.html

敢えて自作をあきらめる手もありますよ^^;

(まっつわん) 2018/04/19(木) 16:59


返事が遅くなり申し訳ありません。

???様
いろいろ検索もしながら下記コードを作成し、フォルダ一覧も取得できました。
あとは取得できた順で判定しながら削除となります。

例えば「C:\TEST(1)\TEST1\Test2」というようなフォルダ構成があり、
基本となるフォルダ(TEST(1))以下がすべて空フォルダの場合には
C:\TEST(1)のみが残るようにするため、
C:\TEST(1)\TEST1\Test2
C:\TEST(1)\TEST1
の順で削除しなければいけないと思うのですが(これはsizeを取得したときに
データが多いフォルダだと時間がかかるため)、階層の深さ順に並べ替える
必要がありますよね?

 Sub TEST2()
    Dim Fl
    Fl = FolderSearch("C:\TEST(1)", "*.*")
    For i = 0 To UBound(Fl)
        Debug.Print Fl(i)
    Next i
 End Sub

 Function FolderSearch(SEARCH_DIR, SEARCH_FILE)
    Dim TMPFile As String
    Dim strCmd As String
    Dim buf() As Byte
    Dim FileNum As Long
    Dim TargetPath
    TMPFile = Environ("TEMP") & "\Dir.tmp"
    strCmd = "Dir """ & SEARCH_DIR & "\" & SEARCH_FILE & """ /b/s/a:d > """ & TMPFile & """"
    With CreateObject("Wscript.Shell")
         .Run "cmd /c" & strCmd, 7, True
    End With
    If Len(Dir(TMPFile)) Then
        If FileLen(TMPFile) < 1 Then
            Kill TMPFile
            TargetPath = Array("")
            Exit Function
        End If
    Else
        TargetPath = Array("")
        Exit Function
    End If
    FileNum = FreeFile
    Open TMPFile For Binary As #FileNum
        ReDim buf(1 To LOF(FileNum))
        Get #FileNum, , buf
    Close #FileNum
    Kill TMPFile
    FolderSearch = Split(StrConv(buf, vbUnicode), vbCrLf)
 End Function

まっつわん様
自社のルールでフリーソフトはNGでした。
手間を考えると有償のソフトウェアの購入を上長に懇願しましたが
結果NGでした。(^^;)

(すけろく) 2018/04/20(金) 09:43


下の階層から消さなければいけないのはその通りですので、一覧を得た後に、何らかの方法で並び替える必要がありますね。 一例を挙げておきます。(削除の代わりに、フォルダ名をシートに表示して終わってます)

 Sub test()
    Const cPATH = "C:\TEST(1)\"
    Dim AR As Object
    Dim cFiles() As String
    Dim i As Long

    Set AR = CreateObject("System.Collections.ArrayList")

    cFiles = Split(CreateObject("WScript.Shell").Exec("CMD /C DIR /A:D/B/S """ & cPATH & "*.*""").StdOut().ReadAll(), vbNewLine)
    For i = 0 To UBound(cFiles) - 1
        AR.Add cFiles(i)
    Next i
    AR.Sort
    AR.Reverse

    For i = 0 To AR.Count - 1
        Cells(i + 1, "A").Value = AR(i)
    Next i
 End Sub

問題は、これに該当するフォルダ数が幾つあるかですねぇ。 数の問題でコントロールが対応できずエラー停止してしまうようならば、何回かに分けて、サブフォルダを親に指定するしかないでしょう。
(???) 2018/04/20(金) 17:46


余談ながら、DIR命令には並び替えの機能があるのですが、これが Samba による共有等(NAS等、Linuxのファイルサーバはほぼこれ)の場合は無視されてしまうので、並び替えが効かない場合が殆どです。 今回の対象もネットワーク先でしょうから、一覧を得た後に並び替えています。

それと、単純な逆順で済ませており、階層の深い順ではないですが、同じ親なら同じ文字列な訳ですし、これで十分ではないかと思います。
(???) 2018/04/20(金) 18:01


 なんとなく書いてみた
 とりあえずは動いた

    Const tFolder = "D:\"
    CMDLine = "CMD /C DIR /A /S " & tFolder & " | FIND /V ""/"""

    Set emptyDirList = CreateObject("Scripting.Dictionary")
    Set DirData = CreateObject("WScript.Shell").Exec(CMDLine).StdOut

    Do Until DirData.AtEndOfStream
        getLine = DirData.ReadLine
        If InStr(getLine, " のディレクトリ") Then
            getPath = Mid(getLine, 2, InStrRev(getLine, " ") - 2)
            DirData.ReadLine
            emptyDirList.Add getPath, CLng(Left(DirData.ReadLine, 16))
        End If
    Loop

    If Right(tFolder, 2) = ":\" Then emptyDirList.Remove tFolder

    pathList = emptyDirList.Keys

    For n = UBound(pathList) To LBound(pathList) Step -1
        fPath = pathList(n)
        If emptyDirList.Exists(fPath) Then
            If emptyDirList.Item(fPath) Then
                Do While emptyDirList.Exists(fPath)
                    emptyDirList.Remove fPath
                    fPath = Left(fPath, InStrRev(fPath, "\") - 1)
                Loop
            End If
        End If
    Next

    Debug.Print Join(emptyDirList.Keys, vbCrLf)

 問題点としてはFAT32だとemptyDirList.Keysのソートが必要になるかも?
(2u) 2018/04/20(金) 20:11

コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.