Windows で WSL + Hyper + fish shell 環境を再構築する

# **WSL**

WSL ( Windows Subsystem for Linux ) は、Windows 上で動作できる Linux ( Unix ) っぽい環境の一つ。

有名どころでは Cygwin や MSYS2 などと競合するが、

- Microsoft が公式に提供している
- Linux Distributions 環境をほぼそのまま使える
- Windows 10 を利用しているユーザであれば、比較的簡単に入れられる

などは強みだ。最近ベータも外れたので、本格導入に向けて再調査した。

https://docs.microsoft.com/en-us/windows/wsl/about

## インストール

[Install your Linux Distribution of Choice](https://docs.microsoft.com/ja-jp/windows/wsl/install-win10)

### WSL の有効化

まずは、有効にするために管理者権限で起動した Powershell で以下を実行する。

```powershell
PS> Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
```

その後再起動すると、WSL が使えるようになる。

### アプリストアから Linux Distribution をインストール

古いバージョンが入っている場合は、最新版をストアから取得する。

#### ● WSL のバージョン確認

古いバージョンは、こんな感じに Legacy と出る。
![before.jpg](https://qiita-image-store.s3.amazonaws.com/0/20198/80759ede-22c8-6d1e-7f78-c0099b6a7318.jpeg)

もう使わないので、先にアンインストールした。

#### ● 最新 WSL のインストール ( Ubuntu の場合 )

<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/5f46656f-70f6-be03-c3d0-2619468c67a1.jpeg" width="400px" />

インストールが完了したら、Ubuntu が規定になっていることを確認する。
![after.jpg](https://qiita-image-store.s3.amazonaws.com/0/20198/d1a94b16-6318-0a69-6101-d8ee328443ba.jpeg)

Ubuntu 以外にも、様々な Linux Distribution が提供されている。

- Debian
- SUSE Linux
- OpenSUSE
- Kali Linux

特にこだわりが無いなら、Ubuntu が最も情報が多く、安心。

## 個人的必須パッケージのインストール

自分が使う上で最低限必要なパッケージを入れておく。

```bash
$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install curl jq git tig openssl ca-certificates build-essential software-properties-common iproute2 

# fzf
$ git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf
$ ~/.fzf/install

# micro
$ cd /usr/local/bin
$ curl https://getmic.ro | sudo bash

# exa
$ curl -LO https://github.com/ogham/exa/releases/download/v0.8.0/exa-linux-x86_64-0.8.0.zip
$ unzip exa-linux-x86_64-0.8.0.zip
$ sudo chown root:root exa-linux-x86_64 && mv exa-linux-x86_64 /usr/bin/exa

# ripgrep
$ curl -LO https://github.com/BurntSushi/ripgrep/releases/download/0.8.1/ripgrep_0.8.1_amd64.deb
$ sudo dpkg -i ripgrep_0.8.1_amd64.deb && rm ripgrep_0.8.1_amd64.deb

# bat
$ curl -LO https://github.com/sharkdp/bat/releases/download/v0.4.1/bat_0.4.1_amd64.deb
$ sudo dpkg -i bat_0.4.1_amd64.deb && rm bat_0.4.1_amd64.deb
```

## 相互運用性

Fall Creators Update や Anniversary Update を経て、Windows と Linux ( WSL ) の相互運用性は大きく高まっていた。

[WSL interoperability with Windows | Microsoft Docs](https://docs.microsoft.com/en-us/windows/wsl/interop#creators-update-and-anniversary-update)

Bash on Windows と呼ばれていた時代は文字通り `bash.exe` で起動していたが、これは現在 Deprecated となっている。
今後は [`wsl.exe` で起動](https://docs.microsoft.com/en-us/windows/wsl/interop#creators-update-and-anniversary-update) する。
`bash.exe` 時代からの違いとしては、以下がある。

- `-c` が要らなくなった
- Windows の `%Path%` を `$Path` に取り込める _(あれ、この機能って昔から無かったっけ？？)_

### ◆ Windows Platform から Linux Platform を利用する場合

Windows 側から Linux コマンドを動かしたい場合、以下のように実行できる。

```powershell
PS> ipconfig | wsl grep IPv4   # この grep は、Linux 上の grep

   IPv4 Address. . . . . . . . . . . .: 172.10.20.30
   IPv4 Address. . . . . . . . . . . .: 10.0.100.200
```

**`wsl` を頭につけるだけ** で呼び出せる。とても楽だし、可読性も損ねてはいない。

#### ● Windows 向けバイナリとの比較

これまで自分は、[scoop を使って Windows 用にコンパイルされた Linux コマンドを山程入れていた](https://qiita.com/kikuchi_kentaro/items/77793e4a21db6ffdb7cd#scoop) 。
しかし、それらは大抵 **起動がワンテンポ遅く**、また一部のコマンドにおいては**バージョンも古かった**り、**動作やコマンド体系・オプションが Linux 版と全然違ったり** と、満足行くものではなかった。

それに比べて今の WSL は、**簡単で、軽量で、**かつ **シームレスに動作** できるものが **公式** に提供されている。
<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/acd5c8ae-0a3d-ba86-2344-d03e0a54851e.png" width="640px" />

乗り換えない理由は無かった。

#### ● Alias 追加

ということで、早速 `wsl <<Linux コマンド>>` を Alias として登録し、よりシームレスに統合する。

Powershell で Alias を登録する場合、Powershell の Profile である `Powershell_profile.ps1` に追加することで可能となる。
自分は別ファイルに切り出しモジュール化し、それをプロファイルで `Import-Module` するようにしている。

```powershell:Wsl.ps1
function wsl_grep() {
  $input | wsl grep $args
}

Set-Alias -Name:"grep" -Value:"wsl_grep" -Option:Allscope
```

```powershell:Powershell_profile.ps1
Import-Module "C:\path\to\Wsl.ps1"
```

これで、ほぼシームレスな連携が可能となる。

```powershell
PS> ipconfig | grep IPv4

   IPv4 Address. . . . . . . . . . . .: 172.10.20.30
   IPv4 Address. . . . . . . . . . . .: 10.0.100.200
```

自分用に使いそうなコマンドの Alias・Functions をまとめた Powershell ファイルを作ったので、参考に。
https://gist.github.com/kentork/bd85a670302a22852b55652afe59ef47

※ `mkdir` のように Windows でも同名のコマンドが存在する場合は、Windows アプリケーションに意図しない影響を与える事もあるので注意。

### ◆ Linux Platform から Windows Platform を利用する場合

今度は、WSL の中から Windows コマンドを利用する場合を考える。
実は、これも簡単にできる。

```bash
$ ipconfig.exe | grep IPv4   # この ipconfig は、Windows 上の exe

   IPv4 Address. . . . . . . . . . . : 172.10.20.30
   IPv4 Address. . . . . . . . . . . : 10.0.100.200

```

ポイントとしては、**Windows のバイナリを動かすときは `～.exe` までしっかり入力** すること。
<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/32534a27-dad0-5811-e396-9464ee115a11.png" width="740px" />

また、`dir /b` などの `/` で指定するオプションは、 `dir -b` のように `/` を `-` に変更する必要があるようだ。

### ◆ どっちがメイン？

CLI 周りのエコシステムを考えると、どうしたって Linux に軍配が上がってしまう。
今は **Linux Platform から Windows Platform を利用する** 方を基軸としている。

とは言え、まだまだ Powershell を使いたいケースはあるので、Windows Platform から Linux Platform を利用する方法も残している。

### パス表現の違い

WSL を運用する上で無視できないのが、この **パス表現の違い** 。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/20198/887bfd94-5e05-0e92-932a-bd7949e52620.png)

見るだけであれば、慣れてくれば頭の中で変換できるようになってくるが、

- Case 1. `wsl rm -r hoge/piyo.json` の `piyo.json` を Tab 補完で見つけようとすると Windows スタイルになってエラー
- Case 2. `wsl find -type f > files.txt` で出力されるファイル一覧が Linux スタイルに

などのケースがよく起こる。地味に辛い。Alias 使ってて意識が向きづらくなると特にハマりやすい。

● Windows 側での対応

一応、変換するための関数を Powershell で自作している。

```powershell:Wsl.ps1
function w2l($in) {
    if ( $in -match "(^[a-zA-Z]:(\\{1,2})?$|^[a-zA-Z]:\\{1,2}[^\\])")  {
      $in = "/mnt/" + $in.Substring(0, 1).ToLower() + $in.Substring(2).Replace('\\', '/').Replace('\', '/')
    } elseif ( $in -match "(^\.(\\{1,2})?$|^\.\\{1,2}[^\\])") {
      $in = $in.Replace('\\', '/').Replace('\', '/')
    }
    return $in
}
function l2w($in) {
    if ( $in -match "(^/mnt/[a-z]/?|^/mnt/[a-z]/[^/])")  {
      $in = $in.Substring(5, 1).ToUpper() + $in.Substring(6).Replace('/', '\')
    } elseif ( $in -match "(^\./?$|^\./[^/])") {
      $in = $in.Replace('/', '\')
    }
    return $in
}
function w2ls() { $input | %{ w2l($_) } }
function l2ws() { $input | %{ l2w($_) } }
```

```powershell
PS> wsl rm -r $(w2l .\hoge)
PS> wsl find -type f | l2ws > files.txt
```

正直、エラーになって初めて気がつき、それから対策として使うケースが多い。
それでもやっていけないわけではないが、根本的な解決策は欲しい。

● Linux 側での対応

2018 年 3 月のアップデートから、Linux 側に `wslpath` というコマンドが追加され、Linux style ⇔ Windows style ができるようになった。

せっかく公式にあるので、Linux 側ではそれを利用している。
( 以下、fish を前提にしている為、他の Shell の場合は読み替えて )

```bash:config.fish
alias w2l='wslpath'
alias l2w='wslpath -w'
function w2ls
    while read -l line
        wslpath $line
    end
end
function l2ws
    while read -l line
        wslpath -w $line
    end
end
```

```bash
$ find -type f | l2ws > files.txt
```

● 環境変数の相互変換

`wsl.exe` になり、 WSL → Windows または Windows → WSL で環境変数が継承されるようになったが、その場合にパス表現をどう引き継ぐのかという問題があった。

これに関し、[ビルド17063 から、`WSLENV` という環境変数が加わり](https://news.mynavi.jp/article/20180122-574925/) 、パス変換のルールを指定できるようになった。
詳しくは[リンク先](https://news.mynavi.jp/article/20180122-574925/)を参照のこと。

# **Hyper**

Hyper は、Windows でも動くクロスプラットフォームなターミナル。

<img src="https://qiita-image-store.s3.amazonaws.com/0/20198/735892da-8107-0a16-22b4-6d47aa030526.png" width="540px" />

https://hyper.is/
https://github.com/zeit/hyper

Powershell などを使う上でも素晴らしいターミナルであるが、今回は WSL と一緒に使う事を前提に設定していく。

## インストール

scoop や Chocolatey でも提供されているので、簡単に入る。

```powershell
PS> scoop install hyper

or

PS> choco install hyper
```

## 設定

Hyper の設定は `~/.hyper.js` で全て管理されているので、それを編集して設定を行う。
自分はこんな感じ。

```diff:.hyper.js
...

- fontSize: 12,
+ fontSize: 16,

- cursorBlink: false,
+ cursorBlink: true,

- copyOnSelect: false
+ copyOnSelect: true

- shell: '',
+ shell: 'C:\\Windows\\System32\\wsl.exe',

...
```

## フォント

フォントは MyricaM を使っている。
[プログラミングフォント Myrica / Estable](https://myrica.estable.jp/)

Powerline 化は、以下リポジトリの手順を参考にした。
[tsunesan3/awsome-jp-coding-fonts](https://github.com/tsunesan3/awsome-jp-coding-fonts)

```diff:.hyper.js
...

- fontFamily: 'Menlo, "DejaVu Sans Mono", Consolas, "Lucida Console", monospace',
+ fontFamily: 'MyricaM, Menlo, "DejaVu Sans Mono", Consolas, "Lucida Console", monospace',

...
```

## プラグイン

Hyper はプラグインで拡張することで機能を追加していく。
とは言え、**現在は Hyper 2.0 移行期で未対応が多かった** り、 **Windows 対応が悲惨なことになっていたり** と、胸が締め付けらる思いがする。

- [awesome hyper](https://github.com/bnb/awesome-hyper)
- [Hyperでイケてるターミナルをつかおう](https://qiita.com/vimyum/items/44478a51ef3a6f49804f)

### ウィンドウ

最前面固定のために、[hyper-always-on-top](https://github.com/chabou/hyper-always-on-top) を入れている。
背景透過もやりたかったが、[hyper-transparent](https://github.com/codealchemist/hyper-transparent) も [hyper-transparent-dynamic](https://www.npmjs.com/package/hyper-transparent-dynamic) も上手く動作しない。

### ステータスバー

大人気の [hyperline](https://github.com/Hyperline/hyperline) は残念ながら [Windows 未対応](https://github.com/Hyperline/hyperline/issues/57)。
今は [hyper-statusline](https://github.com/henrikdahl/hyper-statusline) を使っている。が。これも WSL だと Git 管理下なのに Git 情報が表示されなかったりと不安定。

### タブ

タブ機能の強化としては [hyper-tabs-enhanced](https://github.com/henrikdahl/hyper-tabs-enhanced) が便利だが、[Windows で CPU 使用率が異常に上がる](https://github.com/henrikdahl/hyper-tabs-enhanced/issues/13) 問題があり、使えない。

### テーマ

テーマも Plugin として導入ができる。

- [Theme - Hyper](https://hyper.is/themes)
- [Hyperthemes – Browse all Themes for Hyper](https://hyperthemes.matthi.coffee/)

自分は、[hypermaterial-vibrancy](https://hyper.is/plugins/hypermaterial-vibrancy) とか [hyper-material-theme](https://github.com/equinusocio/hyper-material-theme) を使ったりいる。

## Explorer 統合

Windows を使っていると、フォルダを右クリックして `Open with Hyper` とかしたくなる。

インストールすると追加してくれるらしいが、自分が scoop で入れたときはなかった。
ので、自分で入れた。

```powershell:hyper_integration.reg
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Directory\Background\shell\Hyper]
"Icon"="C:\\Users\\<< YOUR_NAME >>\\scoop\\apps\\hyper\\current\\Hyper.exe,0"
@="Open Hyper here"

[HKEY_CLASSES_ROOT\Directory\Background\shell\Hyper\command]
@="C:\\Users\\<< YOUR_NAME >>\\scoop\\apps\\hyper\\current\\Hyper.exe \"%V\""

[HKEY_CLASSES_ROOT\Directory\shell\Hyper]
@="Open Hyper here"
"Icon"="C:\\Users\\<< YOUR_NAME >>\\scoop\\apps\\hyper\\current\\Hyper.exe,0"

[HKEY_CLASSES_ROOT\Directory\shell\Hyper\command]
@="C:\\Users\\<< YOUR_NAME >>\\scoop\\apps\\hyper\\current\\Hyper.exe \"%V\""
```

こんな感じで追加することで登録できた。

## Tips

### Copy / Paste

`Ctrl+Shift+C` / `Ctrl+Shift+V` で可能

# **fish shell**

細かな設定を行わなくても最初からそれなりに使える Shell。ものぐさな自分に最適。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/20198/1264c102-72c8-8ae1-5468-d71778fd917e.png)

https://fishshell.com/

Windows ではなく Linux を主軸に置きたいと思った要因として、**fish があるから** というのは大きい。 

## インストール

※ 以下、WSL 上で実行している

なぜか GPG エラーで鍵追加できなかったので、ビルドする。

```bash
# 最新のソースを取得
$ cd ~
$ git clone https://github.com/fish-shell/fish-shell && cd fish-shell

# インストール
$ mkdir -p build && cd build
$ cmake -DCMAKE_BUILD_TYPE=Release ..
$ make
$ sudo make install
$ rm -rf fish-shell
```

```diff:.hyper.js
...

- shellArgs: [],
+ shellArgs: ['fish'],

...
```

## 設定

fish の設定は、`~/.config/fish/config.fish` に書く。

```bash:config.fish
# お好みのカスタマイズ
alias ls='exa -l'
alias grep='rg'
alias cat='bat'
function zz
    set p (z -l | awk '{ print $2 }' | fzf)
    if test $status -eq 0
        cd $p
    else
        return $status
    end
end

# Windows 側の Explorer で起動するコマンド
alias ii='explorer.exe'

# Windows のパスに合わせて移動するための関数
function wcd
    if [ $argv[1] = "/" ]
        cd /mnt/c
    else if [ $argv[1] = ~ ]
        cd /mnt/c/users/<<User Name>>
    else
        cd /mnt/c/$argv[1]
    end
end
```

## プラグイン

プラグインを管理するために、fisherman を導入する。

https://github.com/fisherman/fisherman

```bash
# fisher 導入
$ curl -Lo ~/.config/fish/functions/fisher.fish --create-dirs https://git.io/fisher

# 好きな Plugin 導入
$ fisher z fzf edc/bass spin 0rax/fish-bd
```

## テーマ

fish のテーマも入れとく。

- [Themes - oh-my-fish](https://github.com/oh-my-fish/oh-my-fish/blob/master/docs/Themes.md)

[bobthefish](https://github.com/oh-my-fish/theme-bobthefish) はなぜか Powerline が出なかったので、諦めて [agnoster](https://github.com/oh-my-fish/theme-agnoster) にした。

# 悩みどころ

## Q. WSL は普通の Linux として使えるのか

**A. そんなことはない**。

- ネットワーク周り
    - :fearful: [`ss` ( `netstat` ) が使えない](https://github.com/Microsoft/WSL/issues/2249)
- ファイルシステムの違い
    - :hugging_face: [FAT やネットワークドライブなどがマウントできるようになった](https://kledgeb.blogspot.com/2017/04/wsl-84.html)
    - :fearful: [Windows 側アプリから VolFS ( Volume File System ) にアクセスできない](http://ascii.jp/elem/000/001/425/1425512/)
    - :fearful: Linux 側から DrvFs の権限が変えられない
    - :fearful: Windows 側の Symlink にアクセスしようとすると Permission Denied になる
- デバイス認識
    - :hugging_face:  [公式には対応した事になっているが…](https://blogs.msdn.microsoft.com/wsl/2017/04/14/serial-support-on-the-windows-subsystem-for-linux/)
    - :fearful: [一部 USB デバイスが認識できない模様](https://wpdev.uservoice.com/forums/266908-command-prompt-console-bash-on-ubuntu-on-windo/suggestions/16759696-add-usb-device-support)

などなど。再構築を初めてから数ヶ月。その間に引っかかった点だけでもこれだけあった。
とは言え、あまり悲観はしていなくて、**むしろすごいスピートで改善している事に驚く**。

ファイルシステム辺りはある程度は諦めるとしても、それ以外はそのうち解決してくれるだろうと思ってる。

## Q. パフォーマンスはどうなのか

* **_vs_ Windows 向けバイナリ**
    * やっぱりファイル IO 系が遅い？
        * `node/nodejs` リポジトリの `git status` は WSL の方が 3 倍遅かった
        * ripgrep で `rg -t yaml hoge -l` とかすると、ほとんど変わらない ( というか早すぎて良く分からん )
    * ツールの癖をつかめば、上手く並行運用はできそう
* **vs VM, Docker**
    * [この記事で検証されている](https://qiita.com/satoru_takeuchi/items/a54812806bba0eb48f02) が、VM と比べると相当遅いらしい。
    * やはり本格的な開発環境構築などを考えると、まだまだなんだろうか。

**A. 諦めも肝心。癖を掴み、遅いものは大人しく Windows ネイティブ or VM, Docker を使おう。**

## Q. 開発環境はどちらで作るべきか

![image.png](https://qiita-image-store.s3.amazonaws.com/0/20198/82185946-e021-c991-eeec-86af9a42d3c2.png)

これを見れば分かるように、今は Windows 側と Linux 側、どちらにも Node.js が入っている。
さて、日々の開発はどちらですべきなのだろうか。

**A. (暫定) とりあえずどっちも入れておいて、適宜変える。**

これに関しては、Docker や VM なども考慮しベストを探す必要があるので、また別の機会に考える。

# 感想

**使い慣れた Linux Distribution・Shell の使い心地で Windows ファイルシステムが操作できる** という点ではとても良い。

本格的に Linux を使いたいのであれば、足りない部分はどうしても感じてしまう。
そこは無理せず、Docker や VM と併用する必要があるだろう。


# 参考

- [kledgeb](https://kledgeb.blogspot.com/)
- [PowerShellでWindows Subsystem for Linux(WSL)のコマンドのエイリアスっぽいものを作る](https://qiita.com/KyoPeeee/items/76f32ae44c50cba6c900)
- [Windows Subsystem for Linuxのファイルシステムにおける注意点](https://qiita.com/lefb766/items/ca2be2590bd7c22395fd)
- [Windows10で快適なfishシェル環境構築](https://qiita.com/ysks/items/6c9d751d9a3b3b1b8020)
- [WSLでfishをログインシェルにすると起動しない](https://qiita.com/slin/items/2d2c62cc0592eb5fd541)
- [WSL vs VM for 開発作業](https://qiita.com/satoru_takeuchi/items/a54812806bba0eb48f02)
- [Windows Subsystem for Linuxとguest/native Ubuntuの性能をざっくりと比較](https://qiita.com/satoru_takeuchi/items/34e9568a9d9282bb37c2)