訳あって、とあるサーバにおけるファイル使用量の増加傾向を見るため、指定ドライブの階層化にある全ファイルのファイルサイズとタイムスタンプを収集するツールを探したのですが、結局見つかりませんでした。
そこで、仕方なく作成したのですが、似たような事でお困りの方のために、今回公開することにしました。
実行ファイルだけほしい人、ソースコード丸ごとほしい人は、どうぞご活用下さい。バグについては気づいた時点で修正していこうと思います。
ソースコードに登場する「フォルダ階層に遡ってファイルを取得する方法」や、「ファイルサイズやタイムスタンプを取得する方法」については、「【C#】フォルダ配下の全ファイルからファイル情報(ファイル名、サイズ、タイムスタンプ)を取り出すには」という記事で詳しく解説しています。興味のある方は併せてご覧下さい。
コマンドの概要
指定したドライブ又はフォルダ配下にあるファイルのファイル情報を一覧表示するコンソールアプリケーションです。
各項目の区切りの初期値は空白ですが、オプションでカンマやタブを指定することが可能です。
引数を指定しない場合は簡単な説明が表示されます。

コマンドの使い方
使い方はコマンドの直後に検索したいドライブ又はフォルダを指定し、後は必要に応じてオプションを指定します。
FileScanner Path [/S] [/TA /CO] [/FY /FD /FM] [/KB /MB /GB] [/NU] [/NQ]
| パラメータ | 内容 |
|---|---|
| Path | 検索したいドライブ又はフォルダを指定します。 ワイルドカード(*.txt 、data12?.csv など)が使用可能です。 |
| /S | フォルダ階層全てを検索対象とします。 指定しない場合はトップのフォルダのみとなります。 |
| /TA /CO | 各項目の区切り文字を指定します。 指定しなければ半角スペース、/TAの場合はタブ、/COの場合はカンマ区切りになります。 |
| /FY /FD /FM | 作成日時、更新日時、アクセス日時の表示を年や日付け単位に省略します。 /FY は年のみ、/FD は年月のみ、 /FM は年月日のみを表示します。 指定が無い場合、年月日時分秒 が表示されます。 |
| /KB /MB /GB | ファイルサイズの表示単位を指定します。 /KB はキロバイト、/MB はメガバイト、/GB はギガバイト単位となります。 メガバイト、ギガバイトの場合は小数点3桁までを表示します。 指定がない場合はバイト単位となります。 |
| /NU | ファイルサイズの直後にはByte、KByte、MByte、GByteの単位が表示されますが、/NUを指定すると単位を表示しません。 |
| /NQ | ファイル名には半角スペースが含まれる可能性があるため、ダブルクォーテーショで両端を囲んでいますが、 /NQ を指定すると囲まなくなります。 |
このコマンドを実行して得られるファイル情報は次の通りです。
| 列名 | 内容 |
|---|---|
| Path | ファイルのフルパス |
| Len | サイズ |
| Create | 作成日時 |
| LastWrite | 更新日時 |
| LastAccess | アクセス日時 |
出力結果をリダイレクトでファイルにしたものをEXCELで表示すると、以下の様になります。
filescanner E:\写真\オークション > kekka.csv

コマンドのダウンロード方法
プログラムのみ必要は方は、下記からダウンロードできます。
Visual Studio2019のプロジェクト一式
ソースコードに興味がある方、必要な機能が無いので追加したい方、バグが見つかったので修正して使いたい方は、下記からプロジェクト一式をダウンロードしてください。
コマンドのソースコード
以下がソースコードになります。
プロジェクトをダウンロードする代わりに、Visual Studioでコンソールアプリケーションのプロジェクトを作成し、下記ソースをコピペして頂く方法もあります。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace FileScanner
{
class Program
{
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("FileScanner ver 1.0");
Console.WriteLine("--------------------------------------------------------------------------");
Console.WriteLine("FileScanner Path [/S] [/TA /CO] [/FY /FD /FM] [/KB /MB /GB] [/NU] [/NQ]");
Console.WriteLine("");
Console.WriteLine("/S フォルダ配下を全て検索");
Console.WriteLine("/TA タブ区切り");
Console.WriteLine("/CO カンマ区切り");
Console.WriteLine("/FY タイムスタンプを年で表示");
Console.WriteLine("/FM タイムスタンプを年月で表示");
Console.WriteLine("/FD タイムスタンプを年月日で表示");
Console.WriteLine("/KB KBの単位に換算");
Console.WriteLine("/MB MBの単位に換算");
Console.WriteLine("/GB GBの単位に換算");
Console.WriteLine("/NU 単位を表示しない");
Console.WriteLine("/NQ ファイル名をダブルクォートで括らない");
return;
}
//第一引数(パス)を取得
string path = args[0];
//サーチオプションの取得。初期値はトップディレクトリのみ。/Sで配下を全て検索
var searc_option = (FindOption(args, "/S")) ?
SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
//区切り文字の指定。初期値はカンマ、/TAでタブ区切り /CO でカンマ区切り
var delimiter = (FindOption(args, "/TA")) ? "\t" :
(FindOption(args, "/CO")) ? "," : " ";
//日付けのフォーマット指定。
var format = (FindOption(args, "/FY")) ? "yyyy" :
(FindOption(args, "/FM")) ? "yyyy/MM" :
(FindOption(args, "/FD")) ? "yyyy/MM/dd" :
"yyyy/MM/dd HH:mm:ss";
//単位の換算値
double conv = (FindOption(args, "/KB")) ? 1024 :
(FindOption(args, "/MB")) ? 1048576 :
(FindOption(args, "/GB")) ? 1073741824 :
1;
//単位の文字列
var unit = (FindOption(args, "/KB")) ? " KByte" :
(FindOption(args, "/MB")) ? " MByte" :
(FindOption(args, "/GB")) ? " GByte" :
" Byte";
//単位を表示しない
unit = (FindOption(args, "/NU")) ? "" : unit;
//ファイル名をダブルクォートで括らない
var quote = (FindOption(args, "/NQ")) ? "" : "\"";
//コンソールにヘッダを出力
Console.WriteLine(string.Join(delimiter,new string[] { "Path", "Len", "Create", "LastWrite", "LastAccess" }));
//ファイル情報の取得ループ処理
foreach (var file in Files(path, searc_option))
{
//ファイル情報から必要な情報を取り出す
List<string> column = new List<string>();
column.Add(quote + file.FullName + quote);
double val = Math.Round((double)file.Length / conv,3, MidpointRounding.AwayFromZero);
column.Add(val.ToString() + unit);
column.Add(file.CreationTime.ToString(format));
column.Add(file.LastWriteTime.ToString(format));
column.Add(file.LastAccessTime.ToString(format));
//コンソールにファイル情報を出力
Console.WriteLine(string.Join(delimiter, column));
}
}
/// <summary>
/// パラメータが指定されているかをチェック
/// </summary>
/// <param name="args"></param>
/// <param name="option"></param>
/// <returns></returns>
static public bool FindOption(string[] args,string option)
{
for(int i = 0;i < args.Length;i ++)
{
if(args[i].ToLower() == option.ToLower())
{
return true;
}
}
return false;
}
/// <summary>
/// ファイル情報の一覧を取得する。
/// アクセス権限が無いフォルダは無視する(例外を発生させない)。
/// </summary>
/// <param name="path"></param>
/// <param name="searchOption"></param>
/// <returns></returns>
static public IEnumerable<FileInfo> Files(string path, SearchOption searchOption = SearchOption.AllDirectories)
{
string drive = Path.GetPathRoot(path); //ドライブ名を取得
string directory = Path.GetDirectoryName(path); //ディレクトリ名を取得
string filter = Path.GetFileName(path); //ファイル名を取得
//ディレクトリが未指定ならドライブ名をディレクトリ名とする
directory = directory ?? drive;
//ファイル名が指定されているとファイル名を、指定されていなければワイルドカードをフィルターに設定
filter = (filter == "") ? "*.*" : filter;
//フォルダ名が指定された場合
if (! filter.Contains("?") && Directory.Exists(path) == true && drive != path)
{
directory = path;
filter = "*.*";
}
return Files(directory, filter, searchOption);
}
/// <summary>
/// ファイル情報の一覧を取得する。
/// アクセス権限が無いディレクトリは無視する(例外を発生させない)。
/// </summary>
/// <param name="directory"></param>
/// <param name="filter"></param>
/// <param name="searchOption"></param>
/// <returns></returns>
static public IEnumerable<FileInfo> Files(string directory, string filter, SearchOption searchOption = SearchOption.AllDirectories)
{
List<FileInfo> infos = new List<FileInfo>();
//ディレクトリが未指定ならカレントディレクトリを対象とする。
directory = (directory.Trim() == "") ? System.IO.Directory.GetCurrentDirectory() : directory;
//指定されたディレクトリの情報を取得
DirectoryInfo dir_top = new DirectoryInfo(directory);
try
{
//指定されたディレクトリ直下に存在するファイル情報を取得
foreach (var info in dir_top.EnumerateFiles(filter))
{
infos.Add(info);
}
}
catch { }
//取得したファイル情報を返す
foreach (var info in infos)
{
yield return info;
}
//サーチオプションが配下のディレクトリを検索対象にしている場合
if (searchOption == SearchOption.AllDirectories)
{
//指定されたディレクトリ直下にあるディレクトリを全て取得
foreach (var dir_info in dir_top.EnumerateDirectories("*"))
{
infos.Clear();
try
{
//取得したディレクトリ直下から末端までの階層をたぐって全てのファイル情報を取得
foreach (var info in dir_info.EnumerateFiles(filter, SearchOption.AllDirectories))
{
infos.Add(info);
}
}
catch { }
//取得したファイル情報を返す
foreach (var info in infos)
{
yield return info;
}
}
}
}
}
}
ファイル情報の取得方法について
ファイル情報を取得するFiles メソッドですが、引数の異なる2種類を用意しています。
- Files(string path, SearchOption searchOption)
- Files(string directory, string filter, SearchOption searchOption)
実際にファイル情報を取得するのは2つ目の Files メソッドで、内部でEnumerateFiles メソッドを読んでファイル情報を取得しています。
こちらは EnumerateFiles の仕様が、対象フォルダの指定とフィルタの指定を別々で指定する形になっているため、これに合わせています。
一方1つ目の Files は、"d:\Document\*.txt" という風に、フォルダ名とファイル名のワイルドカードを合わせて指定できるようにしたかったので、path の中身を解析して、directory と filter に分割する処理を行わせています。
EnumerateFilesに関する詳細は こちら の記事に記載していますが、ここではアクセス権限の無いフォルダを参照した場合の例外エラーが発生しないよう、try~catch を使って例外を握りつぶしています。
このFilesメソッドを丸ごとコピー&ペーストして、必要なプログラムに張り付けていただければ、そのまま使えます。
コマンドパラメータの判定について
コマンド起動時において、 /S や /TA、/CO など、スラッシュから始まるパラメータが指定できるようになっていますが、Main(string[] args)で渡ってきた引数からパラメータを判定する手段としてFindOption というメソッドを作っています。
これは、option で指定されたパラメータ文字列が、args配列に含まれているかをチェックするという簡単なものです。
/D:1000 とか /T:20 の様に、パラメータの後に可変の値が続く場合は、Splitメソッドで分解して値を取り出すなどの処理が必要になりますが、単純なパラメータ指定だけであれば、この方法でも十分使えるかと思います。
static public bool FindOption(string[] args,string option)
{
for(int i = 0;i < args.Length;i ++)
{
if(args[i].ToLower() == option.ToLower())
{
return true;
}
}
return false;
}
まとめ
今回は、指定したドライブ又はフォルダに対して、階層を下って全てのファイル情報を取得する FileScanner コマンドについて、実行プログラム、プロジェクト一式の公開と、ソースコードの簡単な解説を行いました。
コマンドの出力結果はリダイレクトを使ってCSVファイルとして保存できますので、EXCELなどに読み込んで使用容量の増加推移などをグラフ化するなどの用途に使えます。
ディスク容量が気になる方は、是非ご活用下さい。
コメント