ククログ

株式会社クリアコード > ククログ > Windows 10のWindows Subsystem for Linux(WSL)を日常的に活用する

Windows 10のWindows Subsystem for Linux(WSL)を日常的に活用する

何が便利になる? どんな使い方がある?

日本時間の2017年10月18日、Windows 10 Fall Creators Update(バージョン1709)の提供が始まりました。 このバージョンから「Windows Subsystem for Linux(WSL)」が正式な新機能となっていて(※64bit版限定)、これを活用できるようWindowsアプリのストアで「Ubuntu」「openSUSE Leap」「SUSE Linux Enterprise Server」の各Linuxディストリビューションがインストール可能になっています。

これらは「Linux上で動作するプログラムの開発環境」の選択肢の1つとしてももちろん有用ですが、Windows上での一般的な作業の補助ツールとしての有用性にも着目したいところです。

「Windows上でLinuxのコマンドを動かす」という事自体はWSL以前からも既に実現方法がいくつかありますが、Windowsとの親和性を取るか一般的なLinux環境との互換性を取るかのバランスによって、どの選択肢が最適かが変わってきます。以下の表は、大まかな特徴による分類です。

MinGW Cygwin WSL 仮想化(GUIを使用しない場合)
Windows環境との親和性、Windowsアプリとの連携のしやすさ ×
実行時の速度、メモリ使用効率 ×
Linux環境用のプログラムの動作、互換性 × △(再ビルドが必要) ○(サーバーとしての使用は想定外)

この表に示す通り、WSLは既存の取り組みでそれぞれ諦めざるを得なかった部分の両立を図った新しい選択肢と言えます。 端的に言うと、「WindowsにLinux互換のコマンド操作インターフェースが加わった」状態に近いものとなっています。 Linuxのデスクトップ環境やmacOSでは、「普段の操作はGUIで行いつつも、定型的な処理を素早く済ませたい場合には『端末』のウィンドウを開いて、その中で普段サーバーの操作に使っているのと同様のコマンド操作を行う」という使い方ができますが、WSLによってWindowsでもこれに近いことができるようになります。

WSLそのものの導入手順やWSLの仕組みの概要については、技術情報サイトの記事やまんがでわかるWSLなどに譲る事にして、この記事では具体的な活用事例にフォーカスしてご紹介していきます。

なお、Windows 10 Creators Updateおよびそれ以前のバージョンで利用できる「Bash on Ubuntu on Windows」は、現在Windowsのアプリストアから導入可能な「Ubuntu」とは若干構成が異なります。この記事ではWindowsアプリストアからインストールできる「Ubuntu」を対象に説明していきます。

Linux環境からGitリポジトリを操作する

筆者が最も重宝しているのが、Gitのインターフェースとしての利用です。

コミットログを有効に活用するためにはコミットの粒度を適切に分けることが大事ですが、試行錯誤しながらの開発だと、気がつけばあちこちに複数の意図の変更を行った状態になってしまっているということがあります。このような場合、変更を1つ1つのコンテキストごとに少しずつ分割してコミットするのが望ましいですが、GUIでこれをやるのはなかなか手間がかかりますが、gitコマンドではgit add -pgit commit -pを使って、変更をコミットするかどうかを1行単位で簡単に振り分けることができます。

また、仮想環境で完全なLinuxディストリビューションを動作させる以外の方法(例えばGit for Windowsに付属のMinGWによるコマンド操作など)では、今まではLibreOffice Calcのスプレッドシートの変更点をgit diffで見るような事は簡単にはできませんでした。WSLのUbuntu環境は一般的なUbuntuの環境と非常に近いため、そのような連携も、単に必要なパッケージをaptでインストールするだけで簡単に実現できます。

Linux環境での処理結果をWindowsのクリップボードにコピーする

WSLでは、Windowsのコンソールアプリケーションやコマンドを、シェルのコマンド列におけるパイプラインの一部に組み込むことができます。これにより、標準入力で与えられた内容を取り扱うWindowsネイティブのコマンドやコンソールアプリをBashのワンライナーの中に自然に組み込むことができます。

中でも特に使い出がありそうなのが標準入力の内容をクリップボードにコピーするclip.exeで、(コマンド列) | clip.exeという具合にワンライナーの最後にパイプラインで付け加えるだけで、処理結果をすぐにWindowsアプリケーションに貼り付けることができます。もちろん、それ以外にもnpmgemなどでインストールされたコマンドも自由に連携させられます。

WSLを使いやすくする

実際にWSLをLinuxデスクトップのGNOME端末やmacOSのTerminalのように使おうと思うと、気をつけなければならない点や、若干の準備が必要な部分が出てきます。ここでは、筆者が実際に「GitHubでリポジトリを公開している、RubyGemsのパッケージとしてリリースするコマンドラインツールの開発」を行う中で得た知見を元に(具体的にはtiny-classifierの開発時の経験に基づいています)、お薦めの運用の仕方と環境整備手順をご紹介します。

WSLの導入手順そのものの解説は、この記事では省略します。以下の内容はすでにWSLのUbuntuを起動できる状態になっている事を前提としていますので、まだ準備が済んでいないという方はまんがでわかるWSLなどの記事を参考に、先にストアアプリのUbuntuをセットアップしておいて下さい。

Windowsのファイルシステム上のファイルのパーミッションが妥当な形で見えるようにする(2019年4月10日追記)

既定の状態では、/mnt/以下で見えるWindows側のファイルやフォルダは一律「所有者はroot、グループはroot、パーミッションは書き込み権限があれば777、無ければ555」という見え方になり、しかもchmodchownを実行しても何も起こりません。このままだとツールの都合でパーミッションを設定したくてもできませんし、Gitリポジトリを扱うときに無駄に実行権限付きでコミットされてしまって困ります。

WSLでWindowsのファイルシステムをマウントする時の挙動は、/etc/wsl.confでカスタマイズできます。以下のように設定しておくと、パーミッション変更がメタ情報として保持されるようになり、また、初期状態ではファイルに実行権限が付かないようになります。

[automount]
options = "metadata,umask=22,fmask=111"

この設定を変更した後は、一端すべてのWSLのコンソールを閉じて、サービス一覧から「LxssManager」を再起動するか、Windows自体を再起動しておきましょう。

よく使うファイルやGitリポジトリにすぐアクセスできるようにする

WSLを起動した時のホームディレクトリは、Windowsのユーザーのホームとは別の場所にあります。逆に、Windowsで普段使用しているファイルにはLinux環境からは/mnt/c/Users/(username)/~のようなパスで見えます。

よって、Linux環境とWindowsとの間でファイルをやりとりする場合はこのパスでWindowsのフォルダ上のファイルを参照する事になるのですが、このように深いパスを何度も書くのは非効率的です。

この問題の解決の方向としては、以下の3パターンが考えられます。

この中で最も安全でお薦めしやすいのは、3番目の「Windows側のフォルダへのシンボリックリンクをLinux環境内に作成する」方法です。

まず1番目の「WindowsのユーザーのホームをLinux環境のホームディレクトリにする」という方法ですが、これはお薦めできません。何故かというと、まず先述したとおり、初期状態のWSLではマウントしたWindowsのファイルの所有者やパーミッションがおかしなことになります。この問題は/etc/wsl.confの設定で回避できるのですが、他にも、大文字小文字が異なるだけの名前のファイルを作成しようとしてもWindows側ではそれらが同一視されてしまうという問題があります。そういったWSL固有の問題について、自分で開発するプログラムにおいては気をつける事もできますが、パッケージなどで導入した一般的なツールがそのような特殊な事情を考慮してくれていると期待するのはハイリスクです。

2番目の「Linux環境からはWindowsのファイルに触らないことにして、Linux環境のホームディレクトリの実体へのショートカットを作成してWindows側からファイルを操作するようにする」という方法も、安全ではないためお薦めできません。WSLのUbuntu環境のホームは%AppDataLocal%\Packages\CanonicalGroupLimited.UbuntuonWindows_(ランダムな文字列)\LocalState\rootfs\home\(username)という位置に実体となるフォルダが存在していますが、この実体配下のフォルダやファイルを不用意にWindows側から触ると、Linux環境からファイルが見えなくなったり、Linux環境から見た時の内容とWindowsから見た時の内容が異なるファイルになったり、果ては削除不可能なフォルダやディレクトリができてしまったりと、様々な不可解な事態が発生して収拾が付かなくなってしまいます。この実体フォルダ配下にはWindowsからは絶対に触らないようにしましょう

このように、1番目と2番目の方法は全くそれと意識しない通常の操作を行っていても環境が破壊されてしまうリスクが高いのですが、Windows側のフォルダ配下にあるファイルを意図的に読み取ったり書き込んだりする限りにおいては、そのようなトラブルは起こりません。 ですので、

  • 参照する機会が多いWindowsのファイルやフォルダがある場合は、シンボリックリンクをLinux環境のホーム以下に作成しておく

    • 例えば、C:\Users\(username)\Downloadsに保存したファイルをBashから参照する機会が多いならln -s /mnt/c/Users/(username)/Downloads ~/Downloads、デスクトップ上にあるファイルを参照する機会が多いならln -s /mnt/c/Users/(username)/Desktop ~/Desktop、マイドキュメントならln -s /mnt/c/Users/(username)/Documents ~/Documentsといった具合でシンボリックリンクを用意しておく。

    • コマンドの実行時には、これらのシンボリックリンクをcat ~/Desktop/list.csv | cut -f 2 | sort | uniq -n > ~/Desktop/result.txtのように参照する。

  • Windowsのファイルやフォルダが意図せず実行権限付きでコミットされてしまわないよう、あらかじめgit config --global core.filemode falseとして、ファイルの実行権限の変化をGitで検知しないようにしておく。

    • これをしないまま、Linux環境のホーム以下でgit cloneして作業中だったGitリポジトリをmv repo /mnt/c/Users/Public/などとしてWindows側のフォルダ配下に移動すると、前述した現象により全てのファイルのパーミッションが777に変更されたことになり、全ファイルに実行権限を付与するコミットが行われてしまう危険性がある。

    • ファイルの実行権限を明示的にリポジトリに保存したい場面では、TortoiseGitなどのGUIから操作するか、git update-index --add --chmod=+x path/to/file(実行権限を与える)およびgit update-index --add --chmod=-x path/to/file(実行権限をなくす)を使う。

  • Windowsのフォルダ配下では、ファイル名の大文字小文字の違いやファイルのパーミッションが重要な意味を持つ操作は行わない。

    • 例えばGemやnpmのパッケージのリリース操作を行うと、内容物のパーミッションがすべて777になったパッケージができてしまう。このようなパッケージをsudo gem installsudo npm installでインストールされると、管理者権限がないユーザーが自由に書き換えられる状態のファイルがインストールされてしまうため、セキュリティリスクとなる。

    • パーミッションが意味を持つ操作を行う時は、必ずLinux環境のホーム以下に移動してから実行する。Gemやnpmのパッケージのリリース操作であれば、リリース操作用として、Linux環境のホーム以下にもGitリポジトリをcloneしておく。

とするのが、Linux環境とWindowsの間で共通のファイルやGitリポジトリを操作するときのお薦めの運用と言えます。

実際に、筆者はC:\Users\Public以下に(TortoiseGitでcloneした物も含めて)Gitリポジトリを置き、Windowsのテキストエディタで編集した結果をLinux環境からgit commit -pなどとしてコミットするという使い方をしています。Linux環境上でのパーミッションの表示が常に777になってしまう(chmodでのパーミッション変更ができない)ということと、それ故この位置に置いたリポジトリからはパッケージをリリースできないという点を除けば、概ね支障なく運用できています。

SSHエージェントを使い、秘密鍵のパスフレーズ入力を省略する

2020年2月7日追記:Windows 10 1803以降のバージョンでは、Windowsに最初からサービスとして登録されているOpenSSH Authentication Agentとrupor-github/wsl-ssh-agentを使う方法が利用でき、そちらの方がおすすめです。以下は歴史的資料として参照するに留めてください。

BitbucketやGitHubなどのリポジトリ共有サービスでは、パスワード認証よりも安全な認証方式として、SSHの公開鍵を使った鍵認証を選択できるようになっています。秘密鍵を使うにはパスフレーズの入力が必要ですが、LinuxデスクトップではGNOME KeyringなどのSSHエージェントが、macOSではKeychainという同様の仕組みが導入されているため、ユーザーはいちいちパスフレーズを入力しなくても良いようになっています。大抵はセッション開始時の1回だけパスフレーズを入力してやれば、あとは鍵認証の処理をSSHエージェントが代行してくれるというわけです。

WSLでも当然、GitからのSSH接続で公開鍵認証を使えますが、実際には使い勝手は良くありません。何故かというと、WindowsにはGNOME KeyringやKeychainのような標準的な仕組みが無いからです。そのため、git pushgit pullの度に毎回パスフレーズの入力を求められることになります。

Ubuntuでの秘密鍵の使用の様子 macOSでの秘密鍵の使用の様子

この問題の最も簡単な解決方法は、SSHエージェントを使うというものです。具体的には、~/.bashrcに以下の内容を記載しておくと、Windowsを起動してから最初にWSLのUbuntuを起動したときに1回だけパスフレーズを入力すれば、以後Windowsを終了するまでの間は再度のパスフレーズ入力を求められなくなります(解説)。

SSH_AGENT_FILE=$HOME/.ssh-agent
test -f $SSH_AGENT_FILE && source $SSH_AGENT_FILE
if ! ssh-add -l > /dev/null 2>&1; then
  ssh-agent > $SSH_AGENT_FILE
  source $SSH_AGENT_FILE
  ssh-add $HOME/.ssh/id_rsa # この位置にOpenSSHの秘密鍵があると仮定
fi

WSLでの秘密鍵の使用

Pageantとの連携

ただ、ssh-agentを使う方法には「実際に秘密鍵を使うかどうかに関わらず、WSLのUbuntuを起動すると(最初の1回は)必ず秘密鍵のパスフレーズの入力を求められる」という欠点があります。単にローカルのテキストファイルをシェルスクリプトで加工するためだけにWSLを起動したというような場面でもパスフレーズの入力を求められるので、急いでいるようなときには不便でしょう。

これに代わる筆者のおすすめは、Pageantとweasel-pageantを組み合わせるという運用です。

Pageantは「PuTTY」というWindows用のSSHクライアント用のSSHエージェントですが、TortoiseGitなどにも同梱されており、Windows用のSSHエージェントの代表的な物と言えます。ただし、これ単体で使用するとWSLのUbuntu上のssh-agentとは別に動作することになりますので、それぞれで個別に秘密鍵をロードしなくてはなりません。

PageantとWSL上のSSHエージェントが別々に存在する様子

ssh-agentの代わりにweasel-pageantを使うと、Linux環境からPageantに読み込まれた秘密鍵を使って公開鍵認証を行えるようになります。Windows上でパスフレーズを入力して秘密鍵を読み込ませておけば、Linux環境で改めてパスフレーズを入力する必要はありません。Windows上でPageantと連携して公開鍵認証を行うTortoiseGitなどのアプリを併用するのにも好都合です。

秘密鍵の取り扱いがPageantに集約されている様子

手順としては、まずPageantを導入します。全く初めての利用であれば、PuTTYのインストーラをダウンロードしてインストールすると、インストールされたファイル一式の中にpageant.exeという名前でPageantが含まれています。TortoiseGitを導入済みの場合は、TortoiseGitのインストール先のbinフォルダ内にすでにPageantが存在しています。このpageant.exeへのショートカットをスタートアップに登録して、Windowsログイン時に自動起動するようにして下さい

(なお、この時Pageantのショートカットのプロパティで「リンク先」欄のpageant.exeへのパスの後に半角スペースを空けて秘密鍵のフルパスを指定すると、Windowsログオン時に自動的にその鍵を読み込んでパスフレーズの入力を求めてくるようになります。)

次に、weasel-pageantをインストールします。weasel-pageantのリリースページから最新のweasel-pageant-*.zipをダウンロードして、このファイルの内容をC:\Program Files (x86)\weasel-pageantに展開しましょう。weasel-pageantはWindowsアプリなので、ファイルは必ずWindows側の領域に置いて下さい。Linux環境内に置くと動作しないので注意が必要です。

(2019年4月10日追記)また、`/etc/wsl.conf`でファイルの初期パーミッションを変更している関係で、このままだとweasel-pageantのファイルに実行権限が無いことになります。`cd /mnt/c/Program\ Files\ \(x86\)/weasel-pageant/; chmod +x weasel-pageant helper.exe` として[^0]、各ファイルに実行権限を設定しておいて下さい。

そして、WSLのUbuntuの起動時に毎回weasel-pageantを自動実行するよう、~/.bashrcに以下のような行を追加します(weasel-pageantの設置先パスは実際の位置に合わせて下さい)。

eval $(/mnt/c/Program\ Files\ \(x86\)/weasel-pageant/weasel-pageant -r)

以上で準備は完了です。スタートアップに登録したアプリケーションが起動されるように、一旦ログアウトして再度ログインしましょう。タスクトレイにPageantのアイコンが表示されるようになっているはずです。

Pageantが起動した様子

これをダブルクリックして鍵一覧を表示し、「Add」ボタンをクリックして秘密鍵(※Linux等でOpenSSHの秘密鍵を使っていた場合は、puttygen.exeを使ってPuTTY形式の秘密鍵に変換しておく必要があります)を選択します。パスフレーズを入力して秘密鍵をPageantに読み込ませたら、以後Pageantが終了するまでの間はずっと、Linux環境上での鍵認証にはPageantが使われるようになります。

試しに、WSLを起動して、git clone git@github.com:(username)/(project-name).git のように鍵認証を伴う操作を行ってみましょう。パスフレーズの入力を求められずに処理が進行することを確認できるはずです。

過剰な権限があるディレクトリの存在に起因するRubyの警告を抑止する

WSL上でGemによってインストールされたコマンドを実行すると、warning: Insecure world writable dir /mnt/c in PATH, mode 040777のような警告のメッセージが表示されるようになる場合があります。最も単純な例では、ruby -e 'system("true")'というコマンド列を実行するだけでもこの現象の発生を確認できます。

この現象は、前述の「Windows側のファイルが所有者root、アクセス権777に見える」という問題に起因しています。WSLではWindowsの実行ファイル(「メモ帳」など)を直接起動しやすいように、Bash起動時の環境変数PATHにWindowsの環境変数PATHの内容がインポートされるようになっています(cat file.txt | clip.exeのような操作を行えるのはこのお陰です)。しかし、これによってコマンドの探索対象に加わる/mnt/c/WindowsなどのディレクトリのパーミッションがLinux環境では777と見えるために、Rubyスクリプト内でシェルコマンドを探すためにPATHを走査した時に、Rubyが気を利かせて上記の警告を表示してしまうというわけです。

このPATHの自動インポート機能は他にも様々な部分に影響を及ぼすということで、WSLの公開のIssue Tracker上でも取り上げられています。対策としては、以下のような方法が考えられます。

  • 環境変数PATHにWindowsのパスを含めないようにする。 例えば~/.bashrcの冒頭にexport PATH="$(echo "$PATH" | sed -r -e 's;:/mnt/[^:]+;;g')"と追記しておけば、Windowsの環境変数からインポートされたパスが取り除かれた状態になります。この結果、clip.exeのようなWindowsのコマンドをフルパスを指定しなければ実行できなくなるというデメリットがあります。Windowsのコマンドとの連携を考えないのであれば、この方法がお薦めです。 (レジストリを編集する方法もありますが、筆者環境での検証時には、WindowsアプリストアのUbuntuでは設定が反映されませんでした。)

  • Rubyの既定の起動オプションを指定する環境変数RUBYOPTで、-W0を指定しておく-W0はそのものずばり警告を表示しないようにするという指定です(既定値は-W1で、重要な警告のみ表示されます)。例えば~/.bashrcの末尾にexport RUBYOPT=-W0と追記しておけば、上記の物も含めて煩わしい警告が全て表示されなくなります。ただ、それらの中にはデバッグのために有用な情報が含まれる場合がありますので、それらが得られなくなってデバッグしにくくなるというデメリットがあります。Windowsのコマンドとの連携を積極的に活用したいのであれば、この方法がお薦めです。

コンソールの文字色を見やすくする

これまでWindowsのコマンドプロンプトやその他のコンソールアプリで使われてきた配色には、液晶ディスプレイにおいては色の判別が難しいという問題がありました。PC使用環境の変化を受けてWindows 10 Fall Creators Updateではより見やすい配色への変更が行われています。

しかしながら、この新しい配色は新規インストールの環境にのみ反映され、従来バージョンからWindows 10 Fall Creators Updateに更新した環境では以前のままの配色が使われます。これでは文字が見にくい状態のままとなりますので、文字色をより明るい色に変更しておく事をお薦めします。Cortanaで「regedit」と入力してレジストリエディタを起動し、キーHKEY_CURRENT_USER\Consoleを選択すると、dword型のColorTable00からColorTable15までの16個の値が並んでいます。これらがWindowsのコンソールアプリで使われる共通のカラーパレットになっていますので、以下の通り値の内容を変更します。

  • ColorTable01(暗い青):00800000(10進数のRGB表記のR0/G0/B128に対応)を00ff4221(R33/G66/B255)へ変更。

  • ColorTable09(明るい青):00ff0000(R0/G0/B255)を00ff8021(R33/G128/B255)へ変更。

以上で色の変更は完了です。以後は、WSLだけでなくcmd.exeを起動した場合なども含めて、全てのコンソールアプリの青が明るい青で表示されるようになります。

以前のバージョンからの既定の配色 は移植を変更した状態

他にも見にくい色がある場合や、上記の設定でもまだ暗くて見にくい場合には、色のカスタマイズの例を参考にして、好みで明るめの配色に調整すると良いでしょう。

まとめ

以上、WSLを日常的に使うにあたって躓きやすい部分を中心として、お薦めの設定と運用方法の例をご紹介してみました。

Windowsは不自由な部分もありますが、プリインストールPCの選択肢が豊富だったり、商用の高性能・高機能なアプリケーションやゲームが充実していたり、ハードウェアの最新のドライバが手に入りやすかったりと、純粋な道具としてPCを使いたい需要に対しては依然として有力な選択肢です。それらのメリットを享受した上で、Linux環境のツールとしての自由度の高さを同時に得られる物として、WSLにはおおいに利用価値があります。皆さんもぜひこの機会に、「Windows上のLinux環境」を試してみて下さい。