PowerShellでのファイル検索を楽にする（簡単）

# PowerShellでのファイル検索を楽にする
GUI でファイルを検索して開く簡単な方法は沢山あるのですが、PowerShell では長いコマンドを打たせられ、簡単には行きません。折角良いツールなのにもったいなく感じたので、ファンクションを作成してみる事にしました。

以前、下層ファイルへ簡単にアクセスするファンクション ※1 を作成してみたのですが、
今回はファイルやフォルダを曖昧検索して開くまでをやってみます。

※1 Qiita記事(Mintnode)：PowerShellで lsコマンド を使い易くする

https://qiita.com/Mintnode/items/676b5f6f1f0fda70a792

## 
### 1. 下層ディレクトリ内を検索するコマンドをファンクション化する
まずは下層ディレクトリから特定のファイルを曖昧検索するファンクションをプロファイル（profile.ps1）に作ります。

PowerShellでファイルを検索するコマンドは、
<code>Get-ChildItem -Recurse | Where {$_.Name -like "検索するファイル名"}</code>　です。

:::note info
Get-ChildItem のオプション -Recurse（子ディレクトリを含む）は通常、Cドライブ直下では使用出来ない（アクセス権限を変更すれば別ですが）ので、C:\Users\ 以下で使用します。少々不便ですが簡易的なファンクションなのでご容赦下さい。
:::

試しにコマンドを入力して chrome を検索してみます。

```ruby:PowerShell
# 下層を検索したいディレクトリに移動する
cd C:\Users\

# そのままでは表示されないので、検索したファイルを一旦配列に入れる（曖昧検索のため*で挟む）
$files = Get-ChildItem -Recurse | Where {$_.Name -like "*chrome*"}

# foreachで配列から検索されたファイルのpathを1件ずつ取り出して表示させる
foreach($file in $files){
Write-Host $file
}

# 検索結果
C:\Users\ユーザー名\vimfiles\plugged\vim-airline-themes\autoload\airline\themes\monochrome.vim
```

検索結果が表示されました。

これを一旦ファンクション化してみます。

① 以下を入力して$profileを開く（VScodeを使用する場合）

```ruby:PowerShell
code $profile
```

② ファンクションを記述
```ruby:profile.ps1
# ファンクション名は doko です
function doko{
    # 引数を指定します（$pieceは検索文字列、$pointは初期値'mid'を持つ位置を指定する引数）
    param(
        $piece,
        $point='mid'
    )

    # 検索する文字列を$nameに代入します（左詰め、右詰め、中間）
    switch ($point){
            'left' {$name = $piece + '*';break}
            'right' {$name = '*' + $piece;break}
            'mid' {$name = '*' + $piece + '*';break}
    }

    # Get-ChildItem | Where の検索結果を配列に入れる
    $array_address = Get-ChildItem -Recurse | Where {$_.Name -like $name}

    # foreach で1件ずつ検索結果のアドレスを表示させる
    foreach($buf in $array_address){
        $address = $buf -join""
        Write-Host $address
    }
    
    # 変数を削除する
    $name = $piece = $point = $array_address = $null
    [GC]::Collect()
}
```

$profile を上書き保存します。

③ PowerShell で $profile を読み込む

```ruby:PowerShell
. $profile
```

④ ファンクションを実行する（デフォルト値を指定しているので、第2引数無しで実行出来ます）

```ruby:PowerShell
# chrome を検索、第2引数無し
doko chrome
# 結果
C:\Users\ユーザー名\vimfiles\plugged\vim-airline-themes\autoload\airline\themes\monochrome.vim

# 第2引数 left
doko chrome left
# 結果
表示されない

# 第2引数 right
doko chrome right
# 結果
表示されない

# 第2引数 mid
doko chrome mid
# 結果
C:\Users\ユーザー名\vimfiles\plugged\vim-airline-themes\autoload\airline\themes\monochrome.vim
```

無事検索出来ました。

## 
### 2. 検索結果から番号を指定してファイルを開くプログラムを追加する
ここまででファイルのアドレスを検索するファンクションが作成出来ました。
さて、ファイルが検索出来れば次に行う事は高確率で"開く"なので、検索結果から開ける様にします。

① $profile で作成したファンクションに下記コメント部分を追記

```ruby:profile.ps1
function doko{
    param(
        $piece,
        $point='mid'
    )

    # エラー処理を追加
    try{
    
        switch ($point){
                'left' {$name = $piece + '*';break}
                'right' {$name = '*' + $piece;break}
                'mid' {$name = '*' + $piece + '*';break}
        }
        $array_address = Get-ChildItem -Recurse | Where {$_.Name -like $name}

        # カレントディレクトリのアドレスを変数に代入する
        $locate = Get-Location
        $locate = $locate -join""

        # 検索結果が0件ではない時に検索を実行するためのif文を追加
        if($array_address.Count -ne 0){

            # foreach 内で使用する変数 $a を 0 で初期化
            $a = 0
            
            foreach($buf in $array_address){
                
                # 表示させるファイルパスの行番号を代入する
                $x = $a + 1
                
                $address = $buf -join""

                # 検索結果のパスを短くする
                $address = $address.Replace($locate,"")

                # 検索結果を行番号付きで1件ずつ表示させる
                Write-Host $x '. ' $address

                # 変数 $a を +1
                $a++
            }

            # インプットの変数 $yn を 'y' で初期化
            $yn = 'y'

            # インプット$yn で y 以外が入力されるとプログラムを終了させる処理を追加
            while($yn -eq 'y'){

                # どのファイルを開くか実行者に番号を指定させる
                $ib = (Read-Host どのファイルを開きますか? )

                # [int]::TryParse 判定に使用する変数 $b を 0 で初期化
                $b = 0

                # 入力された値が数値かどうか判定する処理
                if([int]::TryParse($ib,[ref]$b)){

                    # 入力された値が配列の範囲内か判定する処理
                    if(($b -gt 0) -and (($b -lt $array_address.Count + 1) -or ($b -lt 2))){

                        # 入力された数値を使用してファイルを開く
                        $x = $b - 1
                        $address = $array_address[$x] -join""

                        # 拡張子ごとに開くプログラムを指定する場合（無くても問題ないです）
                        if($address.Substring(($address.Length -3),3) -eq ".py"){
                            code $address
                        }elseif($address.Substring(($address.Length -3),3) -eq ".cs"){
                            vim $address
                        }else{

                            # Invoke-Item を使ってファイルやディレクトリを開く
                            Invoke-Item $address
                        }

                    # 入力された値が配列の範囲外だった場合の処理
                    }else{
                        Write-Host 入力された番号にファイルがありません
                    }

                    # 続いてファイルを開くかを入力させる処理
                    $yn = (Read-Host 続けて別のファイルを開きますか? y or n? )

                # $ib が数値では無かった場合処理を終了させる
                }else{
                    break
                }
            }

        # ファイル検索結果が0件だった場合の処理
        }else{
            Write-Host $name を含む名前のファイルは存在しません
        }

    # エラーが発生した場合の処理（簡易的に権限の無いフォルダーで実行された場合のみ想定しています）
    }catch{
        Write-Host 権限のあるフォルダー上で実行して下さい

    # エラー発生時も必ず行う処理
    }finally{
        $name = $piece = $point = $array_address = $locate = $a = $yn = $null
        [GC]::Collect()
    }
}
```

$profile を上書き保存します。

② PowerShell で $profile を読み込む

```ruby:PowerShell
. $profile
```

③ ファンクションを実行する
では、最終テストとして vivaldi を検索してみます。

```ruby:PowerShell
# vivaldi を検索、第2引数無し
doko vivaldi
# 結果
1 .  \.vivaldi_reporting_data
2 .  \Desktop\Vivaldi.lnk
どのファイルを開きますか?:
# 2 を指定
(vivaldiが立ち上がる)
続けて別のファイルを開きますか? y or n?:
# Enter または y 以外の入力 + Enter
ファンクション終了
# y + Enter
処理を継続

# 第2引数 left
doko vivaldi left
# 結果
1 .  \Desktop\Vivaldi.lnk
どのファイルを開きますか?:
# 1 を指定
(vivaldiが立ち上がる)
続けて別のファイルを開きますか? y or n?:
# Enter または y 以外の入力 + Enter
ファンクション終了
# y + Enter
処理を継続

# 第2引数 right
doko vivaldi right
# 結果
*vivaldi を含む名前のファイルは存在しません

# 第2引数 mid
doko vivaldi mid
# 結果
1 .  \.vivaldi_reporting_data
2 .  \Desktop\Vivaldi.lnk
どのファイルを開きますか?:
# 2 を指定
(vivaldiが立ち上がる)
続けて別のファイルを開きますか? y or n?:
# Enter または y 以外の入力 + Enter
ファンクション終了
# y + Enter
処理を継続
```

無事にファイルを開く事が出来ました。

## 終わりに
PowerShell の勉強のために作成した"doko"ファンクションですが、「あのファイルどこ！？」という気持ちをコマンドにぶつけられるので結構気に入っています。良ければ試してみて下さい。