Windows標準機能で最速のファイル検索コマンドをきめろ！！

# はじめに
みなさん、毎日windowsでファイルしてますか！？
私はしています。どこかの試算だとファイル検索に使用している時間は、1年で1か月に相当するとか聞いたことがあります。
そこで、私がWindows最速のファイル検索コマンドを探しました。

## 前提
- Windows
- 閲覧権限があるフォルダ
- Powershell

# Windows 最速コマンド
以下の5つのコマンドで計測しました。
また、計測には`Measure-Command`を使用しています。
 `-Expression`で計測したいコマンドを指定することで計測できます。
使用例: `Measure-Command -Expression { Get-Process }`

| コマンド | 説明 | 使用例 |
| ---- | ---- | ---- |
| Get-ChildItem | PowerShellの標準コマンド `Get-ChildItem`を使い、指定したパターンに一致するファイルを再帰的に探索します。 | `Get-ChildItem -Path $rootPath -Filter $pattern -Recurse -ErrorAction SilentlyContinue` |
| [System.IO.Directory]::EnumerateFiles | .NETのメソッドである `[System.IO.Directory]::EnumerateFiles` を使い、指定したパターンに一致するファイルを再帰的に探索します。 | `[System.IO.Directory]::EnumerateFiles($rootPath, $pattern, 'AllDirectories')` |
| ls | PowerShellの別名（エイリアス）である `ls`（本来は `Get-ChildItem`のエイリアス）を使い、指定したパターンに一致するファイルを再帰的に探索します。 | `ls -Path $rootPath -Filter $pattern -Recurse -ErrorAction SilentlyContinue` |
| DirectoryInfo | .NETの `System.IO.DirectoryInfo`クラスを使い、指定したパターンに一致するファイルを再帰的に探索します。 | `$di = New-Object System.IO.DirectoryInfo($rootPath); $di.GetFiles($pattern, 'AllDirectories')` |
| robocopy | `robocopy`コマンドを使い、指定したパターンに一致するファイルを再帰的に探索します。 | `robocopy $rootPath NULL $pattern /L /S /NJH /NJS /NDL /NS` |

これらのコマンドは、すべてファイルを探索するためのものですが、各コマンドにはその特性や速度、そして使用状況や環境による限定的な問題（OneDriveとの相性など）が存在します。

# スクリプト紹介

## 1. Get-ChildItemによる検索と計測 
基本の検索は`Get-ChildItem`じゃないでしょうか。
PowerShellの標準コマンドで、指定されたパスのアイテム（ディレクトリやファイル）を取得します。
`-Path`で指定されたパス（$rootPath）から、`-Filter`で指定されたパターン（$pattern）に一致するアイテムを再帰的に（`-Recurse`）探索しています。`-ErrorAction SilentlyContinue`はエラーが発生しても処理を続行するためのオプションです。


```powershell
$getChildItemTime = Measure-Command -Expression {
    Get-ChildItem -Path $rootPath -Filter $pattern -Recurse -ErrorAction SilentlyContinue | ForEach-Object {
        $_.FullName
    }
}
Write-Output "Get-ChildItem time: $($getChildItemTime.TotalMilliseconds) ms"
```
## 2. [System.IO.Directory]::EnumerateFilesによる検索と計測 
.NET Frameworkのメソッドで、指定されたパスからパターンに一致するファイルを再帰的に検索します。ここでは`$rootPath`から`$pattern`に一致するファイルを再帰的に`（'AllDirectories'）`探索しています。

```powershell
$enumerateFilesTime = Measure-Command -Expression {
    [System.IO.Directory]::EnumerateFiles($rootPath, $pattern, 'AllDirectories')
}
Write-Output "[System.IO.Directory]::EnumerateFiles time: $($enumerateFilesTime.TotalMilliseconds) ms"

```

## 3. lsによる検索と計測
みなさんおなじみの`ls`ですね、実は検索もできます。
`ls`はUnix系システムで使われるコマンドで、PowerShellでは`Get-ChildItem`のエイリアス（別名）として機能します。このコマンドも`$rootPath`から`$pattern`に一致するアイテムを再帰的に探索します。


```powershell
$lsTime = Measure-Command -Expression {
    ls -Path $rootPath -Filter $pattern -Recurse -ErrorAction SilentlyContinue | ForEach-Object {
        $_.FullName
    }
}
Write-Output "ls time: $($lsTime.TotalMilliseconds) ms"

```
## 4. DirectoryInfoによる検索と計測

`System.IO.DirectoryInfo`は.NET Frameworkのクラスで、ファイルシステムのディレクトリを表します。`GetFiles`メソッドは指定されたパターンに一致するファイルを再帰的に取得します。ここでは`$rootPath`から`$pattern`に一致するファイルを再帰的に取得しています。

```powershell
$directoryInfoTime = Measure-Command -Expression {
    $di = New-Object System.IO.DirectoryInfo($rootPath)
    $di.GetFiles($pattern, 'AllDirectories')
}
Write-Output "DirectoryInfo time: $($directoryInfoTime.TotalMilliseconds) ms"
```

## 5. robocopyによる検索と計測
`robocopy`はRobust File Copyの略で、Windowsで使用される強力なファイルコピーのコマンドです。ただし、ここではファイルのコピーではなく、`/L`オプションによってリストのみを生成してファイルを探索しています。`/S`オプションはサブディレクトリも探索することを意味し、`/NJH`, `/NJS`, `/NDL`, `/NS`はヘッダ、サマリ、ディレクトリ名、ファイルサイズの出力を抑制するためのオプションです。
```powershell
$robocopyTime = Measure-Command -Expression {
    robocopy $rootPath NULL $pattern /L /S /NJH /NJS /NDL /NS | Out-Null
}
Write-Output "robocopy time: $($robocopyTime.TotalMilliseconds) ms"
```



# 最速はだれ！？
では計測していきます！

## Explorerのフォルダ検索時間（ローカルフォルダ）

| コマンド | 実行時間 (s) |
| --- | --- |
| robocopy | 0.233 |
| [System.IO.Directory]::EnumerateFiles | 0.244 |
| DirectoryInfo | 0.280 |
| Get-ChildItem | 0.417 |
| ls | 0.533 |

## OneDriveの検索時間
Onedriveとの相性は相当あるっぽいですね。
| コマンド | 実行時間 (s) |
| --- | --- |
| robocopy | 0.047 |
| [System.IO.Directory]::EnumerateFiles | 8.895 |
| DirectoryInfo | 7.688 |
| Get-ChildItem | 57.780 |
| ls | 70.540 |


## ランキング

| ランキング | OneDriveの検索 | Explorerの検索 |
| --- | --- | --- |
| 1 (最速) | robocopy | robocopy |
| 2 | [System.IO.Directory]::EnumerateFiles | [System.IO.Directory]::EnumerateFiles |
| 3 | DirectoryInfo | DirectoryInfo |
| 4 | Get-ChildItem | Get-ChildItem |
| 5 (最遅) | ls | ls |

これらの結果から、robocopyが最適であることがわかりました！
使ったことなかったし、存在を知らなかった。。

みなさん、あしたからrobocopyを使ってファイル検索しましょう（なくさないようなフォルダ管理をするのが一番良い）

# ソース全体
```powershell
$rootPath = "C:\Users\sakaj\OneDrive - WellBee\" # 探索したいディレクトリのパス
$pattern = "hoge.txt" # 検索したいパターンを指定

# Get-ChildItemによる検索と計測
$getChildItemTime = Measure-Command -Expression {
    Get-ChildItem -Path $rootPath -Filter $pattern -Recurse -ErrorAction SilentlyContinue | ForEach-Object {
        $_.FullName
    }
}
Write-Output "Get-ChildItem time: $($getChildItemTime.TotalMilliseconds) ms"

# [System.IO.Directory]::EnumerateFilesによる検索と計測
$enumerateFilesTime = Measure-Command -Expression {
    [System.IO.Directory]::EnumerateFiles($rootPath, $pattern, 'AllDirectories')
}
Write-Output "[System.IO.Directory]::EnumerateFiles time: $($enumerateFilesTime.TotalMilliseconds) ms"

# lsによる検索と計測
$lsTime = Measure-Command -Expression {
    ls -Path $rootPath -Filter $pattern -Recurse -ErrorAction SilentlyContinue | ForEach-Object {
        $_.FullName
    }
}
Write-Output "ls time: $($lsTime.TotalMilliseconds) ms"

# DirectoryInfoによる検索と計測
$directoryInfoTime = Measure-Command -Expression {
    $di = New-Object System.IO.DirectoryInfo($rootPath)
    $di.GetFiles($pattern, 'AllDirectories')
}
Write-Output "DirectoryInfo time: $($directoryInfoTime.TotalMilliseconds) ms"

# robocopyによる検索と計測
$robocopyTime = Measure-Command -Expression {
    robocopy $rootPath NULL $pattern /L /S /NJH /NJS /NDL /NS | Out-Null
}
Write-Output "robocopy time: $($robocopyTime.TotalMilliseconds) ms"

```