PowerShellの.bashrc的な設定ファイルこと$Profileについての紹介

## [IPFactory Advent Calender 2019](https://qiita.com/advent-calendar/2019/ipfactory) 21日目
IPFactory3年の @smicle です。
Advent Calenderは今年が初参加になります。
何卒よろしくお願いいたします。

## 目次
> - [PowerShellScriptを実行する権限を付与](#powershellscriptを実行する権限を付与)
> - [profileの場所](#profileの場所)
> - [profileからScriptを読み込む](#profileからscriptを読み込む)
> - [aliasの設定](#aliasの設定)
> - [functionの設定](#functionの設定)
>   - [オススメの設定](#オススメの設定)
>   - [alias rm="rm -rf"を定義する](#alias-rmrm--rfを定義する)
> - [プロンプトをカラフルにする](#プロンプトをカラフルにする)
> - [キーボードバインド等を変更](#キーボードバインド等を変更)
> - [所感](#所感)

PowerShellでもaliasとか設定したくないですか？
というか設定しないとまともに作業できない…

PowerShellの.bashrcことprofileを設定しましょう！
[詳細：Windows PowerShell Profiles](https://docs.microsoft.com/en-us/previous-versions//bb613488(v=vs.85)?redirectedfrom=MSDN)


## PowerShellScriptを実行する権限を付与

初期状態だとprofileを設定してもscriptを実行する権限がないので、適切な権限を付与しましょう。
色々ありますけど管理者権限で以下のコマンドを実行すれば大丈夫です。

```powershell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
```

[詳細：Set-ExecutionPolicy](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy?view=powershell-6)


## profileの場所

> profileの場所一覧
>
> - $Profile.AllUsersAllHosts
>   - %windir%\system32\WindowsPowerShell\v1.0\profile.ps1
> - $Profile.AllUsersCurrentHost
>   - %windir%\system32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1
> - $Profile.CurrentUserAllHosts
>   - %UserProfile%\My Documents\WindowsPowerShell\profile.ps1
> - $Profile.CurrentUserCurrentHost
>   - %UserProfile%\My Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1

[詳細：Understanding the Profiles](https://docs.microsoft.com/en-us/previous-versions//bb613488(v=vs.85)?redirectedfrom=MSDN#understanding-the-profiles)

以下のコマンドで場所が確認できます。
表示された**Path**の場所にファイルを作成し編集します。

```powershell
echo $Profile.AllUsersAllHosts
echo $Profile.AllUsersCurrentHost
echo $Profile.CurrentUserAllHosts
echo $Profile.CurrentUserCurrentHost
```

**CurrentUser**の方が影響範囲が狭いので良いと思うのですが、自分は`Documents\WindowsPowerShell`を作りたくないので**AllUsers**の方に作っています。


## profileからScriptを読み込む

直接profileに色々書き込むと場所が指定されてしまったり、管理者権限が必要など不都合が多いので、別ファイルを読み込むようにすると楽です。（しなくても良い）

単一ファイルの場合

```powershell
$script = "$Home\profile.ps1"
if (Test-Path $script) {
  . $script
}
```

複数ファイルの場合

```powershell
$script = @("$Home\profile.ps1", "$Home\function.ps1")
foreach ($s in $script) {
  if (Test-Path $s) {
    . $s
  }
}
```


## aliasの設定

ようやくaliasの設定に入れます…
PowerShellには`touch`がないので設定してみます。（何故ない？）

```powershell
Set-Alias -Name touch -Value New-Item

# 短縮した方が見やすい
Set-Alias touch New-Item
```

`git`を`g`にするならこれ

```powershell
Set-Alias g git
```

以上です。
[詳細：Set-Alias](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/set-alias?view=powershell-6)



ですがこのaliasには一つ問題があります。
引数込みの設定ができない点！

```bash
alias ..='cd ..'
```

みたいなことがPowerShellの**alias**ではできません。
でもご安心下さい！**function**を使えば同じような事ができます。


## functionの設定

ということでfunctionの設定です。
さっそく`..`を設定してみます。

```powershell
function ..() {
  cd ../
}

# 1行でも良い
function ..() {cd ../}
```

以上です。
[詳細：about_Functions](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions?view=powershell-6)


### オススメの設定

エクスプローラーがカレントディレクトリで開きます。

```powershell
function el() {explorer .}
```

カレントディレクトリのpathをクリップボードにコピーします。

```powershell
function pwdc() {Set-Clipboard "$pwd"}
```

ディレクトリを作成したときの結果出力を非表示にします。（touchでも使える）

```powershell
function mkd() {mkdir $args | Out-Null}
```


### alias rm="rm -rf"を定義する

自分は過激派なので`rm`を`rm -rf`に上書きしています。
さっそくやっていきましょう！

PowerShellは普通に`rm -rf`と入力しても動きません。
そもそもPowerShellの`rm`はただ**alias**です。

試しに以下のコマンドを打ってみて下さい。

```powershell
Get-Alias | ? {$_.Name -eq "rm"}
```

このようなメッセージが出たと思います。

```
CommandType  Name               Version  Source
-----------  ----               -------  ------
Alias        rm -> Remove-Item
```

`rm`は`Remove-Item`のaliasです。
`cd`は`Set-Location`、`ls`は`Get-ChildItem`と一般的に使いそうなaliasが元から掛かっています。（何故`touch`がない？）

PowerShellの`rm`は、皆さんの知っている`rm`に似てるようで違います。
[Remove-Item](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/remove-item?view=powershell-6)のドキュメントを見れば分かるはずです。

`-r`は`-Recurse`、`-f`は`-Force`が同様の機能みたいなので

```powershell
rm -Recurse -Force
```

とやれば`rm -rf`と同じ事ができます！

以下の関数を登録すれば完了です。

```powershell
function rm() {Remove-Item -Recurse -Force $args}
```

実際に動作確認をしてみます。

```powershell
rm .\test\

確認
C:\Users\smicle\test\ の項目には子があり、Recurse パラメーターが指定されていませんでした。続行した場合、項目と共にすべての子が削除されます。続行します
か?
[Y] はい(Y)  [A] すべて続行(A)  [N] いいえ(N)  [L] すべて無視(L)  [S] 中断(S)  [?] ヘルプ (既定値は "Y"):
```

`Recurse`は設定されているはずだが?

コマンドには**優先順位**が存在します。

> コマンドの優先順位
> 1. Alias
> 2. Function
> 3. Cmdlet
> 4. Native Windows commands

[詳細：About Command Precedence](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_command_precedence?view=powershell-6)

なので`rm`のaliasを削除しなければなりません。
aliasの削除にも`remove-item`を使います。

```powershell
remove-item alias:rm -Force
```

これで`rm`が`rm -rf`で動くようになります！
なのでprofileには以下のコードを追加すればOKです。

```powershell
remove-item alias:rm -Force
function rm() {Remove-Item -Recurse -Force $args}
```

わざわざ`rm`を上書きする必要はないですが、PowerShellで`rm -rf`するのは面倒くさいので、`rmrf`とか登録しといた方が良いかも。


## プロンプトをカラフルにする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218691/7fc6f697-f9fa-87c3-ec53-05d27ac0168f.png)

白一色で見にくいのでカラフルにしたい！
PowerShellは`prompt`の値を書き換える事で表示を変えられます。

以下のコードを追加すればプロンプトがカラフルになります！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218691/d80cfeb7-5e26-bb59-fe15-eba571eb1a26.png)

```powershell
function prompt {
  Write-Host "$env:USERNAME " -ForegroundColor "Green" -NoNewline
	Write-Host "$pwd " -ForegroundColor Magenta -NoNewline
	Write-Host "$" -ForegroundColor "Green" -NoNewline
	return " "
}
```

`-NoNewline`を外せば2行とかにもできるので、自分好みのプロンプトに変更しましょう。

ちなみに管理者権限か判断する場合、以下のコードで確認できます。

```powershell
$isRoot = (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
```

自分は管理者権限なら赤色、一般権限なら緑色にしたいので以下のようにしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218691/8d4e3e51-7301-bf6b-5416-95af971fc908.png)

```powershell
function prompt {
  $isRoot = (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
  $color  = if ($isRoot) {"Red"} else {"Green"}
  $marker = if ($isRoot) {"#"}	 else {"$"}

  Write-Host "$env:USERNAME " -ForegroundColor $color -NoNewline
	Write-Host "$pwd " -ForegroundColor Magenta -NoNewline
	Write-Host $marker -ForegroundColor $color -NoNewline
	return " "
}
```

[詳細：prompt](https://docs.microsoft.com/en-us/previous-versions//bb613488(v=vs.85)?redirectedfrom=MSDN)


## キーボードバインド等を変更

以下の1行を追加するだけです。

```powershell
Set-PSReadLineOption -EditMode Emacs -BellStyle None
```

`-EditMode Emacs`は、`Emacs`のキーバインドに変更します。
なので、`Ctrl+u`でキャレットより前の文字を全部消したりなど、`bash`と同じ感覚でショートカットが使えます。

`-BellStyle None`は、バックスペース押し続けた際のビープ音が鳴らなくなります。

[詳細：Set-PSReadLineOption](https://docs.microsoft.com/en-us/powershell/module/psreadline/set-psreadlineoption?view=powershell-6)


## 所感

**PowerShellは普通に使える！**
もちろんWSL使った方が良いときもありますが、基本的にPowerShellで問題なく作業できます。
想像以上に機能が多く紹介しきれていない事も沢山あるので、気になったら[PowerShell ドキュメント](https://docs.microsoft.com/ja-jp/powershell/)を見てみて下さい。