PowerShell を最大限活用するための環境構築

[朝日新聞社 Advent Calendar 2021](https://qiita.com/advent-calendar/2021/asahi)の第10日目を担当する清田です。

***「Linux サーバーを使うことは多いが手元で使用しているのは Windows だ」***
という方も多いかと思います。

そのような方は PowerShell を Linux コマンドのように使いこなせると、Windows でも CLI が非常に快適に扱えるようになります。

私は開発用 Mac にも PowerShell をインストールして適宜使用しています。

ということで、本記事では

#### UNIX コマンドにはある程度慣れている方向けの PowerShell を使いやすくするための環境構築

についてお伝えします。
​

## インストール

Windows 10 では PowerShell 5.1 が標準でインストールされていますが、最新の PowerShell をインストールするのが良いです。

https://docs.microsoft.com/ja-jp/powershell/scripting/install/installing-powershell-on-windows

上記の記事にあるように、PowerShellをインストールする手段はいくつかあります。​

- GitHub からダウンロード
  - msi
  - zip
  - (自力でビルド)
- WinGet コマンド(msiインストールと同等)
- Microsoft Store

Microsoft Store からのインストールが最も手軽ではありますが、制限もあります。
常用するならば、GitHub のリリースページから msi ファイルをダウンロードしてインストールするのが推奨されています。
​
WinGet コマンドでインストールするならば、下記のコマンドで済むので簡単です。
(管理者として実行する必要があるかもしれません)

```powershell
winget install Microsoft.PowerShell
```
msi でのインストールならば Windows Terminal は自動で認識してくれるはずです。

​
Mac ならば `brew` を使うのが良いでしょう。

```sh
brew install --cask powershell
```
​
## PowerShell を実行

インストールしてみたら、`pwsh` を起動してみましょう。
Windows の PowerShell 5 までのコマンドは `powershell` でしたが、マルチプラットフォーム化された PowerShell 6 からは `pwsh` となっています。
​
pwsh という略称ですが、PowerShell 5 の頃は posh という略称が一般的でしたが、[posh という名称のシェルが存在しているので pwsh となりました](https://github.com/PowerShell/PowerShell/issues/4214)。
​
まず、バージョンを確認してみます。

```powershell:powershell
PS C:\> $PSVersionTable
​
Name                           Value
----                           -----
PSVersion                      7.2.0
PSEdition                      Core
GitCommitId                    7.2.0
OS                             Microsoft Windows 10.0.19041
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
```

PowerShell はシェルとしての側面と REPL としての側面があります。

たとえば、`$PSVersionTable` は .NET の `System.Management.Automation.PSVersionHashTable` クラスの値を保持している変数です。

.NET の REPL なので、C# と同じようなことができます。

```c#:C#
var runtimeVersion = System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription;
```

```powershell:powershell
PS C:\> $runtimeVersion = [System.Runtime.InteropServices.RuntimeInformation]::FrameworkDescription
PS C:\> $runtimeVersion
.NET 6.0.0-rtm.21522.10
```
​
ちなみに、`System` 名前空間は省略できます。

```powershell:powershell
PS C:\> [Runtime.InteropServices.RuntimeInformation]::FrameworkDescription
.NET 6.0.0-rtm.21522.10
```
​
インスタンスメソッドも使用できます。
​`RuntimeInformation.FrameworkDescription` は文字列型なので `Substring` メソッドが使えます。

```powershell:powershell
PS C:\> [Runtime.InteropServices.RuntimeInformation]::FrameworkDescription.Substring(5, 5) 
6.0.0                                      
```
​
REPL なので電卓代わりにもなります。

```powershell:powershell
PS C:\> 1 + 2 + 3 * 4
15
```
​
PowerShell 内蔵のコマンド以外(実行ファイルなど)は文字列でやりとりする、sh に近い動作です。​

```powershell:powershell
PS C:\> cmd /? | wsl grep ON
CMD [/A | /U] [/Q] [/D] [/E:ON | /E:OFF] [/F:ON | /F:OFF] [/V:ON | /V:OFF]
/E:ON   Enable command extensions (see below)
/F:ON   Enable file and directory name completion characters (see below)
/V:ON   Enable delayed environment variable expansion using ! as the
        delimiter. For example, /V:ON would allow !var! to expand the
reasons, /X is the same as /E:ON, /Y is the same as /E:OFF and /R is the
In a batch file, the SETLOCAL ENABLEEXTENSIONS or DISABLEEXTENSIONS arguments
takes precedence over the /E:ON or /E:OFF switch. See SETLOCAL /? for details.
particular invocation of CMD.EXE with the /V:ON or /V:OFF switch.  You
In a batch file the SETLOCAL ENABLEDELAYEDEXPANSION or DISABLEDELAYEDEXPANSION
arguments takes precedence over the /V:ON or /V:OFF switch. See SETLOCAL /?
invocation of CMD.EXE with the /F:ON or /F:OFF switch.  You can enable
If completion is enabled with the /F:ON switch, the two control
PS C:\> cmd /? | Where-Object { $_.Contains("ON") }
CMD [/A | /U] [/Q] [/D] [/E:ON | /E:OFF] [/F:ON | /F:OFF] [/V:ON | /V:OFF]
/E:ON   Enable command extensions (see below)
/F:ON   Enable file and directory name completion characters (see below)
/V:ON   Enable delayed environment variable expansion using ! as the
        delimiter. For example, /V:ON would allow !var! to expand the
reasons, /X is the same as /E:ON, /Y is the same as /E:OFF and /R is the
In a batch file, the SETLOCAL ENABLEEXTENSIONS or DISABLEEXTENSIONS arguments
takes precedence over the /E:ON or /E:OFF switch. See SETLOCAL /? for details.
particular invocation of CMD.EXE with the /V:ON or /V:OFF switch.  You
In a batch file the SETLOCAL ENABLEDELAYEDEXPANSION or DISABLEDELAYEDEXPANSION
arguments takes precedence over the /V:ON or /V:OFF switch. See SETLOCAL /?
invocation of CMD.EXE with the /F:ON or /F:OFF switch.  You can enable
If completion is enabled with the /F:ON switch, the two control
```
​
## 定番のモジュール
​快適な PowerShell 環境の構築のために、定番のモジュールをインストールしてみます。

​PowerShell を開いて

```powershell:powershell
$PROFILE
```

と実行して表示されるパスにあるファイルが起動時に実行されます。
ここに起動時の設定を記述していきます。

bash の `.bashrc` に近いものです。
​
ここにモジュールの読み込みなどの設定を書きます。

### posh-git

git コマンドの補完をしてくれるモジュール `posh-git` をインストールします。

あらかじめ `PATH` に `git.exe` のディレクトリを追加しておく必要があります。
​
インストールはコマンドひとつです。

```powershell:powershell
Install-Module posh-git
```

PROFILE に下記の記述を追加して、起動時に読み込むようにしておきます。

```powershell:$PROFILE
Import-Module posh-git
```

### oh-my-posh

fish shell の oh-my-fish のように表示をカスタマイズできるモジュールです。

Nerd font というフォントが必要なので <https://www.nerdfonts.com/font-downloads> から好きなものをダウンロードして、Windows Terminal や VSCode などに設定します。

ちなみに、VSCode なら `terminal.integrated.fontFamily` がターミナルのフォント設定です。

```powershell:powershell
Install-Module oh-my-posh
```

インストールが済んだら、テーマを決めます。`Get-PoshThemes` を実行するとデフォルトのテーマを列挙してくれるので、この中から好きなテーマを選びます。

```powershell:powershell
Get-PoshThemes
```

PROFILE で `Set-PoshPrompt` を使って読み込みましょう。

```powershell:$PROFILE
Set-PoshPrompt paradox
```

​テーマのカスタマイズもできるので自作しても良いと思います。

```powershell:$PROFILE
Set-PoshPrompt "$PSScriptRoot\mytheme.json"
```

Nerd font をインストールしない場合は minimal なテーマを読み込むと良いです。

```powershell:$PROFILE
Set-PoshPrompt hotstick.minimal
```
​
## タブ補完
​
.NET SDK(`dotnet`) や AWS CLI(`aws`) などは PowerShell 用のタブ補完機能を備えています。

- AWS CLI

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-completion.html#cli-command-completion-windows

- .NET SDK
 
https://docs.microsoft.com/ja-jp/dotnet/core/tools/enable-tab-autocomplete

​
PROFILE に書いておくと便利です。

```powershell:$PROFILE
Register-ArgumentCompleter -Native -CommandName aws -ScriptBlock {
    param($commandName, $wordToComplete, $cursorPosition)
    $env:COMP_LINE = $wordToComplete
    $env:COMP_POINT = $cursorPosition
    aws_completer.exe | ForEach-Object {
        [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
    }
    Remove-Item Env:\COMP_LINE     
    Remove-Item Env:\COMP_POINT  
}
Register-ArgumentCompleter -Native -CommandName dotnet -ScriptBlock {
    param($commandName, $wordToComplete, $cursorPosition)
    dotnet complete --position $cursorPosition "$wordToComplete" | ForEach-Object {
        [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
    }
}
```
​
## コマンド

### UNIX コマンドとの対応​
PowerShell にも UNIX と同様のコマンドが定義されていたりします。
特に Windows では対応する UNIX コマンドと同名のエイリアスが作成されていたりします。

| UNIX コマンド | PowerShell コマンド        | エイリアス                                                          | 備考                                                                          |
| ------------- | -------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `ls`          | `Get-ChildItem`            | `dir`, `gci`, `ls`(Windowsのみ)                                     |                                                                               |
| `cd`          | `Set-Location`             | `chdir`, `sl`, `cd`                                                 |                                                                               |
| `cat`         | `Get-Content`              | `gc`, `type`, `cat`(Windowsのみ)                                    |                                                                               |
| `head`        | `Get-Content -Head {行数}` | なし                                                                |                                                                               |
| `tail`        | `Get-Content -Tail {行数}` | なし                                                                |                                                                               |
| `cp`          | `Copy-Item`                | `copy`, `cpi`, `cp`(Windowsのみ)                                    |                                                                               |
| `rm`, `rmdir` | `Remove-Item`              | `del`, `erase`, `rd`, `ri`, `rm`(Windowsのみ), `rmdir`(Windowsのみ) | `rmdir` でファイルも消せてしまう                                              |
| `mv`          | `Move-Item`                | `copy`, `cpi`, `mv`(Windowsのみ)                                    |                                                                               |
| `diff`        | なし                       | なし                                                                | `diff` は `Compare-Object`にエイリアスがある(Windowsのみ)。かなり動作が異なる |
| `grep`        | なし                       | なし                                                                | `ripgrep` をインストールするのが良いと思います。                              |
​
エイリアスの一覧は `Get-Alias` で取得できます。
​
エイリアスを追加したい場合は `Set-Alias` を使います。
​
たとえば、`ll` を `Get-ChildItem` のエイリアスにしたい場合は下記のようにします。

```powershell:$PROFILE
Set-Alias ll Get-ChildItem
```
​
### 便利な自作コマンド

`Set-Alias` では収まらないようなコマンドを関数として PROFILE 内で定義しておくと便利です。
​
**カレントの親ディレクトリに移動する `bd` コマンド**

```powershell:$PROFILE
function bd { Set-Location .. }
```
​
**コマンドの定義を表示する `which` コマンド**
​
エイリアスならエイリアスの参照先、パスが通っている実行ファイルならそのフルパス、関数なら関数定義そのものなど、良い感じに表示されます。

```powershell:$PROFILE
function which { (Get-Command $args).Definition }
```
​
### コマンドの引数を忘れたときは
​`Get-Command` コマンドに `-Syntax` オプションをつけて実行するとコマンドの引数を表示してくれます。

```:powershell
PS C:\> Get-Command Push-Location -Syntax
​
Push-Location [[-Path] <string>] [-PassThru] [-StackName <string>] [<CommonParameters>]
​
Push-Location [-LiteralPath <string>] [-PassThru] [-StackName <string>] [<CommonParameters>]
```
​
## ショートカットキー
​`Get-PSReadLineKeyHandler` でショートカットキーの一覧を確認できます。
​
他のシェルに使い勝手を近づけるため下記の設定を追加しています。

```powershell:$PROFILE
# タブ補完を zsh などのものと同様にする
Set-PSReadLineKeyHandler -Chord Tab -Function MenuComplete
​
# Ctrl+d で exit する
Set-PSReadLineKeyHandler -Chord Ctrl+d -Function DeleteCharOrExit
​
# Ctrl+w でカーソル左にある単語を削除
Set-PSReadLineKeyHandler -Chord Ctrl+w -Function ShellBackwardKillWord
​
# Ctrl+w で Get-Command などのハイフンに引っかかるのが気になったので引っかからないようにする
Set-PSReadLineOption -WordDelimiters (Get-PSReadLineOption).WordDelimiters.Replace("-", "")
```
​
## その他のソフトウェア
​PowerShell 専用ではないソフトウェアもインストールしておきましょう。

### less

​Windows には `more` はあっても `less` がありません。

そのため `help Get-ChildItem` などで出るメッセージも読みづらくて困ります。

Git for Windows に内蔵されている `less` をページャーとして使用するようにしましょう。

PROFILE で設定しても良いですが、システム環境変数で `PAGER` の値を `"C:\Program Files\Git\usr\bin\less.exe"` としてしまうのが良いと思います。

```powershell:$PROFILE
# これでも良いが、システム環境変数で設定してしまって良さそう。
$env:PAGER="C:\Program Files\Git\usr\bin\less.exe"
```

### Vim
[Vim](https://vim-jp.org/) をインストールしてパスを通せば、普通に使えます。

私は [Neovim](https://github.com/neovim/neovim) に乗り換えたので `vim` は `nvim.exe` のエイリアスにしています。

```powershell:$PROFILE
Set-Alias vim "C:\MyPrograms\Neovim\bin\nvim.exe"
```

### ripgrep

[ripgrep](https://github.com/BurntSushi/ripgrep) は `grep` の進化版を意図して作られたソフトですが、Windows にはそもそも `grep` が存在しないので代替品としてちょうどよいです。

ダウンロードしてパスを通すだけ使えます。

```powershell:powershell
PS C:\> dir C:\ | rg Program
d-r--          2021/11/30    11:00                Program Files
d-r--          2021/11/30    11:01                Program Files (x86)
```

補完の定義も用意されているので PROFILE で読み込んでおきましょう。

```powershell:$PROFILE
. "C:\MyPrograms\ripgrep\complete\_rg.ps1"
```
​
## 活用する👍

ひととおりの環境構築ができたら PowerShell をどんどん使って行きましょう！
![image (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2277284/afc56d32-cec5-fdab-f6bc-03926ffea69d.png)

***

朝日新聞社では、技術職の中途採用を強化しています。
ご興味のある方は下記リンクから希望職種の募集ページに進んでください。
皆様からのご応募、お待ちしております！

https://www.asahishimbun-saiyou.com/information/career