WSLにもそこそこ使えるopenが欲しい！

これは [dotfiles Advent Calendar 2019](https://qiita.com/advent-calendar/2019/dotfiles) の 18 日目の記事です。

WSL のユーザー向けに、過去の記事よりちょっと便利な `open` コマンドの実装を紹介します。
ちなみに自分の dotfiles は [こんな感じ](https://github.com/ahuglajbclajep/dotfiles) で、デスクトップ版の Ubuntu でも使っています。

# `open` コマンドとは

まず `open` コマンド（Ubuntu などでは `xdg-open` など）とは、指定されたパスや URL をデフォルトのアプリケーションで開くコマンドです。
例えば Ubuntu の場合、`xdg-open .` で Nautilus などのファイラーが、`xdg-open https://qiita.com` で Chrome などのブラウザが、`xdg-open ~/.bashrc` で VS Code などのエディターが起動するはずです。
便利ですね。
しかし WSL の場合は GUI がないため、これらのコマンドが上記のように機能しません。
WSL でも `open` でエクスプローラーや Windows 側の Chrome なり VS Code なりを起動したいですよね？

これについては先人がおり、Qiita にも既に [WSL から open](https://qiita.com/aohmusi/items/a46e3267ab506cedd1ad) という同様の記事が投稿されています。
今回はここで紹介されている `open` の実装を見直し、より便利にアップグレードしてみました。
Windows10 1903 で [WSL 内のディレクトリにも Windows で有効なパスが割り当てられた](https://devblogs.microsoft.com/commandline/whats-new-for-wsl-in-windows-10-version-1903/) こともあり、具体的には以下のようなことが可能になっています。

- スペースを含むパスに対応した
- URL に対応した
- `open /` など Windows 側に実体のないファイルも開けるようになった

# 実装とその説明

とりあえずこんな感じになっています。
WSL ユーザーはこれを `~/.bashrc` に追記し、`source ~/.bashrc` を実行すれば `open` が使えるようになります。

```bash:~/.bashrc
open() {
  if [ $# -ne 1 ]; then return 1; fi
  if [ -e "$1" ]; then
    local winpath=$(readlink -f "$1" | xargs -0 wslpath -w)
    powershell.exe start "\"${winpath%?}\""
  else
    powershell.exe start "$1"
  fi
}
```

まず 1 行目ですが、リリース情報を見て WSL かどうかの判定をしています。
判定方法は [いくつか](https://moyapro.com/2018/03/21/detect-wsl/) あるようですが、今回は POSIX 準拠でなるべくわかりやすそうなものを選択しました。
（まあ後で 3 か所ほど Bash の拡張を使ってしまっているのであまり意味はないかもですが。）
次の `if` は引数が一つだけかのチェックで、一つでないときは early return しています。
このあたりまでは元の実装と大体同じ雰囲気です。

次の `if` では指定されたパスが存在するかを確認し、URL などパスでない場合は `else` でそのまま `powershell.exe start` を呼び出しています。
元の実装はワンライナーでこの部分がなかったため、URL を処理できませんでした。
また元の実装では `cmd.exe /c start` を使っていましたが、今回は [4U6U57/wsl-open](https://github.com/4U6U57/wsl-open) を参考に `powershell.exe start` を使っています。
入力がパスであった場合の処理も変更しており、ワンライナーをやめローカル変数 `winpath` を作ってから `powershell.exe start` にこれを渡すことで、`powershell.exe start` による余計なログが出力されないようにしています。
（なぜローカル変数を作ることで余分なログが出なくなるのかは、正直よく分かっていません。）
ちなみに `winpath` とは、`wslpath -w` で指定された WSL 側のパスを Windows 側のパスに変換したものです。
`wslpath` の前に `readlink -f` でシンボリックを展開し、`xargs` でコマンドをつなぐとき `-0` でスペースを含むパスも扱えるようにしています。
あとは最後に `"\"${winpath%?}\""` で、`start` が正しく動作するよう両端に `"` をつけたり `-0` によるヌル文字を消したりしています。

# 拡張子とアプリの関連付けを変更する

`open` で起動するアプリと拡張子との関連付けは、Windows10 の「設定」の「[既定のアプリ](ms-settings://defaultapps)」の下の「ファイルの種類ごとに既定のアプリを選ぶ」をクリックした画面で変更できます。

# おまけ
自分は `if uname -r | grep -q 'Microsoft'; then` のブロックにこんなコードも書いています。

```bash:~/.bashrc
if uname -r | grep -q 'Microsoft'; then
  export LS_COLORS='ow=01;33'

  ggl() {
    if [ $# -eq 0 ]; then return 1; fi
    open "https://www.google.com/search?q=$(echo "$@" | sed 's/+/%2B/g;s/ /+/g')"
  }

  if [ "${PWD,,}" = '/mnt/c/windows/system32' ]; then cd; fi
fi
```

まず最初の `export LS_COLORS='ow=01;33'` は `ls` で表示されるフォルダーの色を変更する設定です。
[この記事](https://www.kwbtblog.com/entry/2019/04/27/023411) を参考に、`/mnt/c` 以下にある Windows 側のフォルダのシンボリックリンクを見やすいように黄色にしています。

次の `ggl` は以下のツイートを参考に作った関数で、ターミナルから Google 検索ができるようになります。
`open` は `/mnt/c/Program\ Files\ \(x86\)/Google/Chrome/Application/chrome.exe` で置き換えても動きます。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">こういうシェルスクリプト作っておくとターミナルからググれるので便利 <a href="https://t.co/BDSroyqKJR">pic.twitter.com/BDSroyqKJR</a></p>&mdash; うじまる🐔 (@uzimaru0000) <a href="https://twitter.com/uzimaru0000/status/1189705915001257984?ref_src=twsrc%5Etfw">2019年10月31日</a></blockquote>

最後の `if [ "${PWD,,}" = '/mnt/c/windows/system32' ]; then cd; fi` は [Windows Terminal](https://github.com/microsoft/terminal) から WSL を使っている人向けの設定です。
VS Code のように右クリックのメニューから Windows Terminal を開けるようにした場合、ショートカットから起動すると `/mnt/c/WINDOWS/system32` などがカレントディレクトリになってしまうのを修正します。
詳しくは [microsoft/terminal#1060](https://github.com/microsoft/terminal/issues/1060#issuecomment-559625588) を見るといいでしょう。
余談ですが Windows Terminal は現状でもかなり便利なのでおすすめです。
自分の dotfiles にも [Windows Terminal の設定](https://github.com/ahuglajbclajep/dotfiles/blob/master/other/profiles.json) が置いてあるので、興味のある人は是非使ってみてください。

# 参考文献

- https://qiita.com/aohmusi/items/a46e3267ab506cedd1ad
- https://devblogs.microsoft.com/commandline/whats-new-for-wsl-in-windows-10-version-1903/
- https://moyapro.com/2018/03/21/detect-wsl/
- https://github.com/4U6U57/wsl-open
- https://stackoverflow.com/questions/52691835/wsl-ubuntu-how-to-open-localhost-in-browser-from-bash-terminal
- https://www.kwbtblog.com/entry/2019/04/27/023411
- https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-5.1#parameters
- https://docs.microsoft.com/ja-jp/windows/uwp/launch-resume/launch-settings-app#apps
- https://twitter.com/uzimaru0000/status/1189705915001257984
- https://github.com/microsoft/terminal/issues/1060#issuecomment-559625588
- https://qiita.com/kawaz/items/211266021515b3f033a3