WSL から open


2019/12/18　ググれるようになりました　→　[新しい記事](https://qiita.com/aohmusi/items/3651a84a63551ae36ff1)


# はじめに
せっかく [Windows Subsystem for Linux \(WSL) を使えるように設定](https://docs.microsoft.com/en-us/windows/wsl/install-win10)して、コードを書いていても、Windows 側のフォルダを開いたり、ファイルを Windows 側で標準に設定したソフトで開くのが面倒です。（ただし WSL 内のファイルを Windows から開くことはできません[^1]。）
[^1]: [WSL 2 では可能になる](https://docs.microsoft.com/en-us/windows/wsl/wsl2-about)ようです（仮想マシンを使うらしいので当然ですが）。
マウスでいうダブルクリック感覚で、ターミナル上でファイルやフォルダを実行すると標準ソフトが起動する Mac の open や Linux の xdg-open みたいな便利コマンドが欲しいものです。
誰しも同じようなことを考えていて、これまでにも色々と提案されています
1. [cmd.exe /c start](https://qiita.com/chez_sugi/items/603a0ba8016aaae9ef98)
2. [さらに cmd.exe の存在を確認](https://qiita.com/suzuki_sh/items/b1dbf8cff27e1196ed8c)
3. [wsl-open](https://github.com/4U6U57/wsl-open) (xdg-open と連帯)
4. [wsl-terminal](https://github.com/goreliu/wsl-terminal) (wsltty の拡張)

WSL 内のファイルは Windows 側から編集できないので Windows 側のディレクトリをシンボリックリンクで WSL に繋げると便利です。具体的には

```bash
ln -s /mnt/c/Users/windows_username/Downloads ~/Downloads
```
というふうにホームフォルダにシンボリックリンク ~/Downloads を作ります。

１を拡張してシンボリックリンクに対応させた関数 open はこれまでにもありました。
1. [open.sh を自作して使う](https://ptsv.jp/scripts/wslstart/)
2. [上のpathを修正](https://qiita.com/sh0510/items/4dc645c3a47c07e49c0f)
この記事で紹介するのは、これらの .bashrc 版だとご理解ください。

# 提案法

以下では、コード全体を紹介した後に zsh での使用例、コマンドごとに分解した説明をします。コマンドごとの説明はくどいかもしれませんが、理解せずに .bashrc を書き加えるのは避けるべきでしょう。ひとたび理解すれば自分流にカスタマイズできますので、辞書代わりだと思って既にご存知の方もご了承ください。

## bash script 全体
まずは bash script 全体を御覧ください。

```bash:~/.bashrc
if [[ $(uname -r) =~ Microsoft$ ]]; then
  function open(){
    if [ $# -eq 1 ]; then
      readlink -f $1 |xargs wslpath -m| xargs cmd.exe /c start
    else
      echo "ERROR: argument is missing."
      echo "Please specify file/path to execute by Windows"
      echo "open [file/path]"
    fi
  }
fi
```
ターミナルを起動するたびに上の script をコピペして実行しても使えますが、面倒なので ~/.bashrc に上のコードを加筆（なければ作る）しましょう。 source で読み込む

```bash:terminal
source ~/.bashrc
```
かターミナルを再起動すれば使えます。

### zsh での使用例
[zsh では suffix alias を使えます。](https://qiita.com/matsu_chara/items/8372616f52934c657214)

```zsh:~/.zshrc
alias -s {ppt,pptx,xls,xlsx,doc,docx}=open
```
こうすることで Microsoft Office 製品がファイルを実行するだけで開きます。

## 部分解説
### WSL のときのみ実行
```bash:bash
$(uname -r) =~ Microsoft
```
uname コマンドはOS情報を取得するためのコマンドです。
単にこのコマンドを使っただけでは Linux （Mac だと Darwin ）と表示されるだけなので、リリース情報を表示するオプション -r をつけて Microsoft の文字列をバージョン情報を出力、 ~= でマッチングしています。（Microsoft が Linux ディストリビューションのバージョン情報の仕様変更した際に書き直さなければいけなくなるかもしれません。）

[ここ](https://wpdev.uservoice.com/forums/266908-command-prompt-console-windows-subsystem-for-l/suggestions/34330297-a-command-to-confirm-that-linux-is-running-on-wsl)では Pavel A さんが他のやり方を紹介しています。

```bash:bash
$(cat /proc/version_signature) =~ Microsoft
$(mount) =~ C:
```
前者は、バージョン情報の書かれたファイル version_signature を表示(cat)して Microsoft という文字列が見つかれば真、後者は、マウント情報を表示して C: ドライブがあれば真となるコマンドです。仕様変更で動かなくなった場合に試してみてください。


### 実行条件
```bash:~/.bashrc
function open(){
  if [ $# -eq 1 ]; then
    readlink -f $1 |xargs wslpath -m| xargs cmd.exe /c start
  else
    echo "ERROR: argument is missing."
    echo "Please specify file/path to execute by Windows"
    echo "open [file/path]"
  fi
}
```
引数の数 $# が 1 のとき以外はエラーを吐く関数 open を定義しています。エラー文は適当に書きましたので好きに変えてください。
例えば、

```bash:terminal
open ~/[path/file]
open ./[path]
open
open ~/[path/file] ./[path]
```
のうち、最初の2つ以外は引数が１ではないので下記の else に行きエラーを吐いて実行しません。１行目は Windows 側で設定した標準ソフトでファイルを開きます。２行目は Exploer が起動して指定したディレクトリを開きます。[start](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/start) には他の使い方もありますので、仕様に合わせて改善の余地ありです。

### WSL/Command Prompt 固有のコマンド
#### wslpath
```bash:terminal
wslpath -m ./[path]
```
実行するコマンドには WSL 固有のコマンド [wslpath](https://laboradian.com/wslpath-command-for-wsl/) が含まれます。
このコマンドは WSL の path を Windows の path （または逆）に変換するコマンドです。オプション -m は WSL の path を Windows のスラッシュ / を使った path に変換する際使います。

1. このコマンドを挟まないと /mnt なんて知らないよ！って言われます。start は Command Prompt のコマンドなので当然ですね（後述）。
2. wslpath -w で Windows 固有のバックスラッシュ \ を使った path では \ が無視されてしまいます。（制御文字と認識しているようです。 \\ \\ だとうまくいきます）

Windows の path をスラッシュ / で表現するのは気持ちが悪いですが内部でうまく変換してくれているようです。

#### cmd.exe

WSL では .exe とついている Windows 実行ファイルはそのまま実行できます。
cmd.exe は Windows command interpreter を実行するためのファイルで、引数がなければ Windows のコマンドプロンプトにシェルが切り替わります。後ろについている /c は文字列で表現されたコマンドを実行し、そのあと終了するためのオプションです。

#### start
start は Command Prompt から別ウインドウで Command Prompt を開いて指定したプログラムを実行したりディレクトリを開いたりするための便利コマンドです。

どちらのコマンドもコマンドプロンプトで

```powershell:powershell
cmd /?
start /?
```
を実行すると詳細が表示されます。

### bash コマンド
```bash:terminal
readlink -f
xargs
```
#### readlink
readlink はシンボリックリンクからリンク情報を取得するために使います。 -f フラグで絶対パスが得られます。

#### xargs
xargs は直前のコマンドが出す標準出力を引数として、直後のコマンドを実行します。例えば

```terminal:terminal
> echo -n 'Hello World'
Hello World>
> echo -n 'Hello World'|xargs echo
Hello World
>
```
echo は文字列を出力するコマンドですが -n をつけて実行すると出力後改行しません。その文字列を xargs で受け取って echo を -n をつけずに実行するとちゃんと改行されました。

# まとめ
WSL 用の擬似的な open コマンド（すでに紹介されていたものを .bashrc 用に改造）を紹介しました。いつも Qiita にはお世話になっているので、この記事がみなさまのお役に立てていれば幸いです。誤植・改善点などありましたらご連絡ください。

# おまけ
Mac の open と違って、ここで定義した open では url には非対応です。しかし、 Google Chrome を実行するエリアス

```zsh:~/.zshrc
alias chrome='/mnt/c/Program\ Files\ \(x86\)/Google/Chrome/Application/chrome.exe'
```
（chrome.exe へのパスは確認してから使いましょう）を設定しておくと

```bash:terminal
chrome google.com
```
だけで Chrome のタブが開きます。便利ですね。