質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Q&A

5回答

15240閲覧

【VBA】サブディレクトリも含めたファイル一覧を素早く取得したい

退会済みユーザー

退会済みユーザー

総合スコア0

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

0グッド

2クリップ

投稿2017/07/16 12:31

以下のSample1Sample2はどちらもC:\Tempのサブディレクトも含めたファイル一覧を取得する関数です。
Sample1は'Sample2'よりも実行時間が短いですが、コマンドプロンプトが表示されてしまいます。

質問①Sample1でコマンドプロンプトを画面表示させない方法はありますか?
質問②Sample1,Sample2以外に素早くファイル一覧を取得できるような方法はありますか?

Sub Sample1() Dim WSH, wExec, sCmd As String, Result As String Set WSH = CreateObject("WScript.Shell") sCmd = "dir /B /S C:\Temp" Set wExec = WSH.Exec("%ComSpec% /c " & sCmd) Do While wExec.Status = 0 DoEvents Loop Result = wExec.StdOut.ReadAll MsgBox Result Set wExec = Nothing Set WSH = Nothing End Sub
Sub Sample2() Call FileSearch("C:\Temp") End Sub Sub FileSearch(Path As String) Dim FSO As Object, Folder As Variant, File As Variant Set FSO = CreateObject("Scripting.FileSystemObject") For Each Folder In FSO.GetFolder(Path).SubFolders Call FileSearch(Folder.Path) Next Folder For Each File In FSO.GetFolder(Path).Files Debug.Print File.Path Next File End Sub

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

質問1は分かりません。

質問2は、Sample2は書き方を工夫すれば少し速くできますね。

VBA

1Option Explicit 2 3Sub Sample2() 4 Dim fso As New Scripting.FileSystemObject 5 FileSearch fso.GetFolder("C:\") 6End Sub 7 8Sub FileSearch(ByVal Folder As Scripting.Folder) 9 Dim File As Scripting.File, SubFolder As Scripting.Folder, f As String 10 On Error Resume Next 11 For Each SubFolder In Folder.SubFolders 12 FileSearch SubFolder 13 Next 14 For Each File In Folder.Files 15 f = File.Path 16 Next 17End Sub 18 19’参照設定で Microsoft Scripting Runtime にチェックを入れてください。 20 21’実行結果(C:\ をスキャン。出力はせず変数に格納するのみ。) 22’オリジナル 849秒 23’改良後 720秒 24 25’改良箇所 26’①参照設定をしてVariantの代わりにその型を使う 27’②引数をByValで渡す 28’③引数でFolderを渡す

VB.NET を使うともっと速くできます。

VB.NET

1Imports System.Collections.Concurrent 2Imports System.IO 3 4Module Module1 5 6 Private FileNameBag As New ConcurrentBag(Of String) 7 8 Sub Main() 9 Dim sw = Stopwatch.StartNew 10 Scan(New DirectoryInfo("C:\")) 11 Dim fileNames = FileNameBag.ToArray 12 Array.Sort(fileNames) 13 Dim scanTime = sw.Elapsed.TotalSeconds 14 sw.Restart() 15 For Each fileName In fileNames 16 Console.WriteLine(fileName) 17 Next 18 Dim outputTime = sw.Elapsed.TotalSeconds 19 Console.WriteLine("ファイル数=" & fileNames.Length & " スキャン時間=" & scanTime & " 出力時間=" & outputTime) 20 Console.ReadLine() 21 End Sub 22 23 Private Sub Scan(directory As DirectoryInfo) 24 Try 25 Parallel.ForEach(directory.GetDirectories, Sub(subDir) Scan(subDir)) 26 For Each file In directory.GetFiles 27 FileNameBag.Add(file.FullName) 28 Next 29 Catch 30 End Try 31 End Sub 32 33End Module 34 35’実行結果 36’ファイル数 358,553個 37’スキャン時間 5.98秒 38’出力時間 16.37秒

上記はサンプルですが、Windows アプリにして、コマンドラインパラメータ経由でスキャンするディレクトリ名を入力し、一時ファイルに結果を出力すればコマンドプロンプトを出さずにVBAから容易に利用できると思います。

投稿2017/07/16 17:49

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/07/16 20:57

とてもご丁寧なご回答ありがとうございます。 助かります!
guest

0

Sample1 ですが、VBAではなくWindowsの機能を使っているので実行速度は速いですが、マクロとしては
「使わない方がいい」ですね(コードの可読性・セキュリティが低くなる)。

もし出力先を「Debug.Print」にするSample2でしたら、
(前提:ブックのVBEで「ツール」→「参照設定」→「Microsoft Scripting Runtime」にチェックを入れる)

ExcelVBA

1 2Sub Sample2() 3 Call FileSearch("C:\Temp") 4End Sub 5 6Sub FileSearch(Path As String) 7 Dim FSO As New FileSystemObject, Folders As Folders, Folder As Folder, Files As Files, File As File 8 'Set FSO = CreateObject("Scripting.FileSystemObject")→テキストでは 9 '「オブジェクト参照回数の増加により、マクロ実行速度低下の原因」となるとのこと。 10 Set Folders = FSO.GetFolder(Path).SubFolders 11 For Each Folder In Folders 12 ' Call FileSearch(Folder.Path)→自身のプロシージャを再帰呼び出ししているため、 13 '低下の原因となっている。 14 15 For Each File In Folder.Files 16 Debug.Print File.Path 17 Next File 18 19 Next Folder 20 21End Sub

こちらで実験しましたが、実行速度及び結果も問題無いと思います。
いかがでしょうか?

投稿2017/09/19 04:49

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

VBAのDir関数を使って、フォルダー配下を再帰的に呼び出してフォルダー階層を取得するサブルーチン(WalkFolderList)を作ってみました。
Dir関数の使い方については、Dir関数でサブフォルダを取得するで調べるのが良いと思います。

作成した関数は、探索するフォルダーの最上位のフォルダーを"C:\Program Files\Windows NT"のように引数で指定するとその配下のフォルダーをカンマで繫いで表示する。WalkFolderList関数です。

[例]
WalkFolderList("C:\Program Files\Windows NT\で呼び出すとその配下のフォルダーをカンマで繫いで、次のような文字列で返却するものです。
"C:\Program Files\Windows NT,C:\Program Files\Windows NT\Accessories,...

コードは以下のようになります。Dir関数を用いて指定されたパス直下のファイル名やフォルダー名からフォルダー名を抜き出し、新しくそのフォルダー名からパスを作り出し自分自身(WalkFolderList)を呼び出すものです。

Public Function WalkFolderList(ByVal s As String) As String Dim i As Integer Dim v() As String Dim folderList As String Dim buf As String ' 指定されたパスがディレクトリでなかったら何もせずに空文字列を返却する。 If (GetAttr(s) And vbDirectory) = vbDirectory Then WalkFolderList = s Else WalkFolderList = "" Exit Function End If ' 該当フォルダー直下のフォルダーを動的配列の「v」に格納する。 buf = Dir(s, vbDirectory) i = 0 Do While buf <> "" If buf <> "." And buf <> ".." Then If (GetAttr(s & buf) And vbDirectory) = vbDirectory Then ReDim Preserve v(i) v(i) = s & buf i = i + 1 End If End If buf = Dir() Loop If i <> 0 Then '該当フォルダー直下のフォルダーの下にフォルダーがあるか自分自身の関数「WalkFolderList」を呼び出す For i = LBound(v) To UBound(v) WalkFolderList = WalkFolderList & "," & _ WalkFolderList(v(i) & "\") Next i End If End Function

投稿2017/09/18 12:56

diracpaul

総合スコア157

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

質問②に対する回答ですが、
FSOよりDirを使った方が、数倍早くなります。

後は、検索用に再帰の関数を作って、
サブディレクトリの中も処理していけば、
指定パス以下、全ファイルのパスが取得できるようになります。
※ある程度のプログラミング技術が必要です。

投稿2017/09/13 02:32

ExcelVBAer

総合スコア1175

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

質問1:
Exec()を使うと、コマンドプロンプトは隠せないようです。
代わりに、Run()を使うように、と先行事例で書いてありました。
stackoverflow

質問2:
ueiさんの回答(細かくてバッチリですね!)を参照ください。

投稿2017/07/16 21:59

編集2017/07/16 22:18
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/07/16 22:00

2006年のマイクロソフトの回答も同じようなことが書いてありました。 例が分かりにくいのでURLは端折ります。
退会済みユーザー

退会済みユーザー

2017/07/16 22:10

Runメソッドは戻り値が整数(終了コードかな?)なので、Dirコマンドで取得したファイル一覧を出力することができないです・・・。 一時ファイルにDirコマンドの結果を出力して、そのファイルを読み込んで出力するしかないですかね。
退会済みユーザー

退会済みユーザー

2017/07/16 22:24

使ったことはないのですが、 You'd have to redirect the output to a temporary file that your VBScript could then open, read, and delete. Yet another possibility is to use the Windows clipboard. You can pipe the output of your command to the clip.exe utility. Then, retrieve the text via any number of available COM objects that can access the contents of the clipboard. runを使うと答えも隠れてしまうので、VBSに出力するか、クリップボードにしまうといいよ、とのことでしたので書いた次第です。 (フォルダリストでもできるかどうかの真偽は不明です。)
退会済みユーザー

退会済みユーザー

2017/07/16 22:44

クリップボードの仕組みは全然詳しくないですが・・・ クリップボードだとマクロ実行している間に、ユーザに他のアプリでクリップボードを使用されてしまうと上書きされてしまうような懸念があります(>_<)
退会済みユーザー

退会済みユーザー

2017/07/16 23:08

コマンドプロンプト(クリップボード無し)で完結させようとすると以下の感じでしょうか。 dir /s /b *.*>list.txt # ファイルを書き出してから云々はやっぱり美しくないですね。
退会済みユーザー

退会済みユーザー

2017/07/16 23:21

そうですよね。私もそう思います。 VBSの出力って具体的にどうやるんですかね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問