MacOS ユーザが WSL では無い Windows のコンソール環境を整える

先日、メインの開発環境を MacOS から Windows 10 Professional へと移しました。理由としては主に2点で、現在仕事を自宅の固定席で行っており PC を持ち運びする必要がなくなったため Mac より高速で安価な Windows デスクトップ機を使いたいこと(Ryzen 9使いたい!)、WSL2 が正式版となり使ってみた感じ問題なく WSL2 で仕事の開発ができそうだったことが挙げられます。

WSL2 はふつうに Linux なので問題なく開発環境の構築が行なえ、Windows からも VSCode Remote のおかげでで違和感なくWSL2上のコードを編集、実行ができ快適な開発が行えています。(なお、WSL2 についての記事は山程溢れているので、ここでは殆ど触れません。)

しかしながら、WSL2 ではないふつうの Windows 上で開発する機会が出てきたので、MacOS や Linux のコンソールに慣れた自分でも、快適に操作できる Windows のコンソール環境を整えてみました。

gyazo.com

ざっくりというと

  • シェル
    • bash/zsh から PowerShell Core へ
  • ターミナル
    • Terminal.app から WindowsTerminal へ
  • パッケージマネージャ
    • Homebrew から Scoop へ

を利用しています。

Windows 開発に慣れていないため、慣れている人ならすぐ解るであろう「これ Windows でどうやるんだろう?」がわからず、また PowerShell での操作にも全く慣れていないため、いつもの bash/zsh だとこうできるんだけどなぁ、というのも当初はわからず大変でした。.bashrc 的なものは何?や、$HOME のような環境変数は何?どれ?などなど。しかしながら使ってみると割と早くに慣れることができました。

また、長らく群雄割拠状態だった、Windows のターミナルも Microsoft が謹製の WindowsTerminal を出したことから、多機能ではないけれど殆どの場合において必要十分なターミナルが気軽に使える感じになったと思います。

そしてパッケージマネージャの Scoop。5年前に一度 Windows の開発環境を作ったときは、Chocolatey をメインのパッケージマネージャとして使っていたのですが、パッケージのインストールに管理者権限が基本必要で、また登録されていたパッケージもそんなになく…という感じだったのですが、Scoop は野良パッケージ含め様々なパッケージがあり、いろいろなものをサクッと入れられ、また管理者権限を必要とせずにパッケージを入れられるのも嬉しいですね。

というわけで、本記事ではこれらを使い、少しずつパッケージを入れていき、開発環境を整えていきます。なおOSの環境は Windows 10 Professional バージョン 2004 (ビルド 19041) です。

Scoop のインストール

なにはともあれ Scoop のインストールです。Scoop は PowerShell(以下PS) のみならず cmd.exe でも使えるパッケージマネージャです。入れたパッケージには Path が通るので、WSL2 側からも叩いたりもできます。

PowerShell を立ち上げて、

# ローカルスクリプトの実行ポリシーの許可
$ Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
# https://get.scoop.sh をダウンロードして文字列として評価して実行することでインストールする。
# Invoke-Expression は eval のようなもの。
$ Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')

でインストール完了です。これで PS でも cmd.exe でも scoop コマンドが使えるようになり、例えば

$ scoop install git vim neovim nvs ruby wget curl

などでガッとパッケージをインストールすることができます。

PowerShell Core のインストール

今手元で立ち上げていた PowerShell はバージョン5で、v6 からはクロスプラットフォームの Linux, MacOS 等でも動く PowerShell Core という新しいものになってます。PowerShell v5 では起動時に

新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6

なメッセージが表示されてますね。PS の実行プログラム名も別物で、 powershell.exe が ~v5、pwsh.exe が v6 以降の PowerShell Core です。とりわけ昔の PS を使わないとうまく行かないケースは、新規に Windows に開発環境を整える場合ほぼ無いと思うので、最新の v7 を入れましょう。

早速、scoop で最新のバージョンの PowerShell Core ( pwsh.exe ) を入れます。

$ scoop install pwsh
$ scoop which pwsh
~\scoop\apps\pwsh\current\pwsh.exe

scoop はユーザのホームディレクトリ(PS では環境変数 $env:USERPROFILE で、Linux における $HOME 的なもの) 以下にアプリケーションをインストールするため、インストールに管理者権限は必要ないです。

これで powershell.exe から pwsh と打ち込んでも立ち上がりますし、Windows 左下のタスクバー検索ボックスから PowerShell Core など打ち込んでも立ちがります*1ね。立ち上がると先ほどとはバージョンが異なる PS が立ち上がるでしょう。今後はこちらの PS Core で作業をしていきます。

PS の CUI 上で動くをエディタ入れる

PS の CUI 上のエディタでファイルを編集したい、そんなときは vim を入れましょう。

$ scoop install vim
$ vim hello.txt

vim でなく VSCode に慣れている人は

$ code hello.txt

で(CUIではないですが) VSCode で編集するのもよいでしょう。

Windows Terminal のインストール

続いて Windows Terminal (以下 WT ) を入れましょう。こちらも scoop でインストールします。Windows Terminal はデフォルトで利用できる scoop の main バケットにはなく、extras バケット(主に extras バケットには GUI アプリケーションが含まれます、Homebrew における cask のような感覚)、にあるので、extras バケットを追加してインストールします。

また bucket の追加や更新に git が必要なので、このタイミングで git も入れてしまいます。

$ scoop install git
$ scoop search windows-terminal
'extras' bucket:
    windows-terminal (1.1.2021.0)
$ scoop bucket add extras
$ scoop install windows-terminal

でインストールされました。これで WindowsTerminal.exe を実行すると立ち上がりますね。なお、この scoop で入れた WindowsTerminal は GitHub で公開されている最新版で、Microsoft Store から提供されているバージョンとは異なる(Storeのほうが現在古いです)ので、そちらのバーションを使いたい方は MS Store からインストールしても良いでしょう。

Windows Terminal を立ち上げ、の隣の のようなボタンから「PowerShell Core」を選ぶことで、PSCore でタブが起ち上げられます。

Windows Terminal の設定で、PS Core をデフォルトに

標準で Windows Terminal で立ち上がるシェルは環境によって異なるので、PS Core をデフォルトにしましょう。Ctrl-, で設定が JSON ファイルで開かれるので、好きなエディタで編集します。

   // デフォルトを PS Core の GUID に変更
    "defaultProfile": "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
  // ...
        "list": [
            // list の要素ごとに guid はユニークなので、別の設定とかぶっていたら削除する
            {
                "guid": "{574e775e-4f2a-5b96-ac1e-a2962a402336}",
                "hidden": false,
                "name": "PowerShell Core",
                // https://github.com/PowerShell/PowerShell/issues/11314
                // プロセスの起動時間が長いと Ctrl-C で端末ごと終了するため
                // source をコメントアウトして、commandline をフルパスで記述
                // "source": "Windows.Terminal.PowershellCore"
                "commandline": "%USERPROFILE%\\scoop\\apps\\pwsh\\current\\pwsh.exe",
                "startingDirectory": "%USERPROFILE%"
            },

なお、バットノウハウとして、scoop で入れた pwsh.exe では Ctrl+C を押すと起動時間が長いプロセスは端末ごと終了してしまうため、それを回避するため commandline にフルパスを指定しています。

.bashrc 的な profile.ps1 を編集する

さて、これで WT 上で PS Core (pwsh.exe) が使えるようになりました。続いて PS が立ち上がるときに自動で読み込まれる、 .bashrc 的なプロファイルファイルを編集しましょう。

このプロファイルのファイルは全部で4つあります

$ echo $PROFILE.AllUsersAllHosts
$ echo $PROFILE.AllUsersCurrentHost
$ echo $PROFILE.CurrentUserAllHosts
$ echo $PROFILE.CurrentUserCurrentHost

この中で、「このマシンのこのユーザのみ」のファイルに当たる、$PROFILE.CurrentUserCurrentHost に設定を追加していきます。

直接

$ vim $PROFILE.CurrentUserCurrentHost

みたいにファイルを編集しても良いですし、設定ファイルを git や Dropbox で管理している人は、. dot sourcingを使って、別ファイルを読み込み形にしても良いでしょう。

私は OneDrive で管理しているので

. $env:USERPROFILE\OneDrive\development\powershell\user_profile.ps1

と、OneDrive 以下に user_profile.ps1 ファイルを作って、そいつを $PROFILE.CurrentUserCurrentHost から読み込むようにしています。

$ Add-Content -Value "`r`n. $env:USERPROFILE\OneDrive\development\powershell\user_profile.ps1`r`n" -Encoding utf8 -Path $Profile.CurrentUserCurrentHost

などと打って、ファイルを作成(追記)しても良いでしょう。これで bashrc 的な起動時に読み込まれるプロファイルを編集する準備ができましたね。

oh-my-posh で PS の見た目をいい感じにする

さて、そろそろシックな見た目に飽きてきたと思うので、良い感じの見た目に変更しましょう。

oh-my-posh という、oh-my-zsh のような、いい感じの見た目にしてくれるライブラリ(モジュール)があるのでそれをインストールましょう。また posh-git という、git の状態を取得できるPSモジュールも要求するので、両方入れます。

なお、oh-my-posh も posh-git も scoop から入れられるのですが、両方とも PowerShell Gallery に登録されており、かつoh-my-poshのサイトにも PS Gallery からのインストール方法が書いてあるため、今回は PS Gallery から入れます。

$ Install-Module posh-git -Scope CurrentUser
$ Install-Module oh-my-posh -Scope CurrentUser

Install-Module コマンドレットで入れることができます。なお、まだ NuGet が入っていない場合は、それもインストールするか尋ねられると思うのでインストールしましょう。

続いて pwsh.exe を使ってる場合は、PSReadLine の 2.0.0 (現在はβ版) 以降が必要なため

$ Install-Module -Name PSReadLine -AllowPrerelease -Scope CurrentUser -Force -SkipPublisherCheck

で最新の PSReadLine のベータ版もインストールします。

これで、PS 上で

$ Import-Module posh-git
$ Import-Module oh-my-posh
$ Set-Theme Paradox

と打つと、テーマを切り替えることができます。また、Get-Theme でテーマ一覧を見ることができるので、好みのテーマを見つけ たら、.bashrc 的な profile.ps1 に設定を記述しましょう。

Import-Module posh-git
Import-Module oh-my-posh
Set-Theme robbyrussell # お好きなテーマ
$ vim $PROFILE.CurrentUserCurrentHost
もしくは
$ vim $env:USERPROFILE\OneDrive\development\powershell\user_profile.ps1

ファイルの編集後は、. $PROFILE.CurrentUserCurrentHost を PS 上で打つことで設定を反映できますね。なお、テーマによっては Powerline Font を使っていてうまく表示されない文字がありますが、それらのフォント設定は後ほど行います。

bash (Emacs) 風キーバインドにする

PS で操作をしていると、例えば Ctrl-a が選択になったりと、bash との操作の違いに戸惑います。しかしながら、PSReadline のオプションを変更することで、bash (Emacs) 風キーバインドに変更できます。

ついでに Bell 煩いので消すのと、Ctrl+d を DeleteCharOrExit (罠ぽい…)から DeleteChar にしてしまいましょう。プロファイルに記述します。

Set-PSReadLineOption -EditMode Emacs
Set-PSReadLineOption -BellStyle None
# 標準だと Ctrl+d は DeleteCharOrExit のため、うっかり端末が終了することを防ぐ
Set-PSReadLineKeyHandler -Chord 'Ctrl+d' -Function DeleteChar

これで bash ぽい感じのキーバインドのなります。C-r などのHistory履歴検索も問題なく使えますね。またこのオプションを指定すると、Tab での補間関数も変わって、こちらも bash ぽい補完になります。

$ Get-PSReadLineKeyHandler -Key Tab
Completion functions
====================

Key Function Description
--- -------- -----------
Tab Complete Complete the input if there is a single completion, otherwise complete the input with common prefix for al
             l completions.  Show possible completions if pressed a second time.

なお他にも、MenuComplete なメニューUIでの補完関数の実装もあるので、好みに合わせて切り替えましょう。

# EditMode Emacs 標準のタブ補完
Set-PSReadLineKeyHandler -Key Tab -Function Complete
# メニュー補完に変更
Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete

なお、PSReadLine は PS のカスタマイズでよく使う ( Set-PSReadLineKeyHandler でキーバインドを好きな用にしたり) ので、PSReadLineのドキュメント の一読をおすすめします。MS はドキュメントがしっかりしてる感ありますね。

ちなみに、コレ系の PS コマンドレットは -? で引数ヘルプを見ることができます。-h じゃないので注意。また Get-Help Set-PSReadLineKeyHandler -Online という感じで Get-Help commandlet -Online と打つとオンラインドキュメントが(あれば)ブラウザで開かれるので、そちらで詳細を読めたりします。

git 周りの設定

さて、続いて git 周りの設定です。

git+ssh 周りの操作のために、環境変数 GIT_SSH に windows の OpenSSH を指定します。PS 上で利用する環境変数なども、.bashrc に記述するような感じでプロフィールに書きましょう。PS での環境変数は $env:XXX となります。

$env:GIT_SSH = "C:\WINDOWS\System32\OpenSSH\ssh.exe"

ssh の鍵関係はふつうに ~/.ssh/ に作ります。これで問題なくうまくいく、と思いきや ssh-agent を使っていると

   warning: agent returned different signature type ssh-rsa (expected rsa-sha2-512)

という warning が出ることがあります。この warn は Windows 同梱の ssh ではまだ治っておらず、最新の MS が提供している Win32-OpenSSH では治ってるようなので、warning が出る場合はこちらを入れましょう。

$ scoop install win32-openssh
...
Run 'sudo C:\Users\username\scoop\apps\win32-openssh\current\install-sshd.ps1' to install sshd and ssh-agent as a service.
# ↑コマンドを実行してサービスを登録するために、sudo を入れる
$ scoop install sudo
# ssh-agent をサービスに登録する
$ sudo ~\scoop\apps\win32-openssh\current\install-sshd.ps1

あとは、ssh-agent が使えるように、Windows サービスの「OpenSSH Authentication Agent」が自動起動するようにしておきましょう(上記コマンドはサービスに登録されるだけで、自動起動はされません)。これで、git (+ssh) + ssh-agent 周りも問題なく利用できるようになるはずです。

sudo を入れる

そしてついで、といってはなんですが、上記設定時に scoop install sudosudo も入れたため、PS や cmd.exe で使い慣れた sudo コマンドを使って管理者権限で実行できるようになりました。やった!

シンボリックリンクを使う

現在、Windows では一般ユーザはシンボリックリンクが標準では使えません。そのため、git clone で落としてきたソースの symlink がうまく動かず困るケースがあります。

の 'Allowing non-administrators to create symbolic links' で書かれている方法で symlink 許可を行えるようにするか、Windows の「開発者モード」を有効化する必要があります。私は「開発者モード」の影響範囲をちゃんと把握してないため、ローカルグループポリシエディタ ( gpedit.msc )で自分の利用ユーザに symlink 許可を追加しています。

それに加え、~/.gitconfig に

 [core]
   symlinks = true

を追加することで、git clone 時にうまく symlink が動くようになるでしょう。なお git-for-windows のドキュメントの通り、ファイルシステムが NTFS でないと symlink はうまく作成できないので注意です。また、OS が Windows 10 Home エディションの場合は、グループポリシエディタ等が使えないのですが、Polsedit を使うことで同等の設定ができるようです(未確認)。

GitHub のソースコードを ghq + fzf (peco) でサクッと落とし、ディレクトリの移動をしたい

個人的に開発時に手放せないツールとして、ghq と fzf (もしくは peco) の利用が挙げられます。ghq でローカルに github のソースコードを落としてきて、fzf (や peco) でサクッとディレクトリ移動してソースコード閲覧・編集したいのです。

まずは各種ツールのインストールです。

$ scoop install fzf ghq
# 👇 お好みで。私は ghq で落としたソースコードを ~/src に置いています。
$ git config --global ghq.root ~/src

これで ghq +fzf でのディレクトリ移動が、PS 環境でもあっけないほど問題なく使えますね!

$ ghq get https://github.com/x-motemen/ghq
$ cd "$(ghq root)\$(ghq list | fzf)"

これで github のソースコードディレクトリへの移動がめちゃ楽になりました。続いて、長いコマンドを打ちたくないので、 cd "$(ghq root)\$(ghq list | fzf)" を短いタイプで打てるように登録します。

 function gf {
       cd "$(ghq root)\$(ghq list | fzf)"
 }

これで PS 上で gf コマンドを実行できるようになりました。なお、これでは ghq list | fzf のキャンセル時にも cd されてしまうため、もうちょっとまともな関数にします。

 function gf {
    $path = ghq list | fzf
    if ($LastExitCode -eq 0) {
        cd "$(ghq root)\$path"
    }
}

bash に慣れてる方は、あれ、alias じゃないの?と思われるでしょうが、PS の alias (Set-Alias / Get-Alias) は、引数なしの単純なエイリアスには使えるのですが、複数コマンドを組み合わせたり、引数がある場合は function として登録します。他にも、例えば ghq を --shallow 引数付きで呼び出したい ghg コマンドを追加したい場合

function ghg {
       ghq get --shallow $args
}

などで $args を使いつつ登録します。他にも標準入力を示す $input や /dev/null にあたる $null 等々、表す実態や変数自体は違えど、sh と同等の事ができるものもいくつかあります。これらと function の登録をうまく使えば、bash での alias での、やりたいことの大半はできるようになるでしょう。

関数の動作を確認して問題がなかったら、プロフィールにこれらの関数を書き込むことで、PS 起動時からこれらのコマンドを使えるようになります。

cd したディレクトリ履歴に fzf で移動したい

もう一つ、私がよく使っていたものとして、移動したディレクトリの履歴に fzf で cd することです。これをするには、cd したらどこかに履歴を保存する必要があるのですが、それをいい感じにやってくれる PS Module の ZLocation (z コマンドが使える!)があるのでインストールしましょう。

Install-Module ZLocation -Scope CurrentUser

その後、プロフィールでモジュールを読み込みます。

# oh-my-posh の読み込み後に記述しないとうまく動かないかも
Import-Module ZLocation

これで、cd するとデータベースに書き込まれ z コマンドで履歴一覧が表示されます。この出力を元に fzf で絞り込んで cd をするような function を書いてももちろん良いのですが、PSFzf という fzf と PS をいい感じにつなぎ込んでくれるライブラリがあるので、こちらを入れます。

Install-Module PSFzf -Scope CurrentUser

すると、Invoke-Fuzzy* 系の Function が使えるようになり、その中に Invoke-FuzzyZLocation 関数があり、ZLocation の履歴のリストに cd ができます。他にも Invoke-Fzf ( | fzfを例外処理したり、文字エンコーディングまわり処理したりと、よりいい感じにできる)や、Invoke-FuzzyKillProcess (プロセスリストから kill )等々もあるので、便利に利用できますね。

PS のキーバインドで、fzf まわりの関数を呼び出したい

さて、先程の gf 関数だったり、Invoke-FuzzyZLocation だったりをキーバインドで立ち上げたいですね(bash における bind, zsh における bindkey)。私は bash/zsh では Ctrl-]Ctrl-;で fzf 関係のコマンドが立ち上がるようにしていたので、それを PS でもセットしてみます。

# 実行後入力待ちになるため、AcceptLine を実行する
Set-PSReadLineKeyHandler -Chord 'Ctrl+]' -ScriptBlock { gf; [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine() }
Set-PSReadLineKeyHandler -Chord 'Ctrl+;' -ScriptBlock  { Invoke-FuzzyZLocation; [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine() }

これで ghq + fzf や z でのロケーション履歴検索 + fzf を Ctrl+]Ctrl-; ですぐに立ち上がるようになりました。なお、Chord には Ctrl+t,n など、組み合わせたキーの登録もできるので、結構複雑なキー操作も登録できそうです。

Powerline のフォントを使えるようにする

さて、git 周りもいい感じに使えるようになったので、oh-my-posh のテーマで一部文字化けていた箇所を直すため、Powerline のフォントを使えるようにしましょう。Powerline を使わない、という人はスキップして問題ありません。

といってもフォントのインストールは簡単で

$ ghq get https://github.com/powerline/fonts.git
# 👆 の fonts ディレクトリに移動して↓を実行する。gf を使えば移動がかんたんですね!
$ ./install.ps1

で自動でフォント群をインストールしてくれます。これで * for Powerline というフォント群が使えるようになります。これを WT 設定の fontFace に指定して利用します。なおどれでも好みのものを使えばよいのですが、私は Source Code Pro for Powerline を利用しています。

これで WT の見た目もまぁまぁいい感じになりましたね。

Windows Terminal のタブ切り替えをキーボードショートカットに割り当てる

MacOS / Linux ではターミナル上の screen / tmux 上でシェルを起動しており、C-t + [数字キー] でのシェルの切り替え、C-t + n/p での前・後ろへのウィンドウ移動を行っていました。PS では screen / tmux の代替が無いようなので、Windows Terminal のタブで同じような操作を実現してします。

Windows Terminal では現在 C-t+キー といった複数入力によるキーボードショートカットは割り当てられないようなので、Alt+数字キー,Alt-n/p/t/w等、Altキーとなにかのキーの組み合わせでタブの作成、切り替え、終了などを行うようにしています。また、コピー&ペーストは Alt+c/vに割り当てています。

👆の設定を含むキーボードショットカットの例です。

なお、WindowsTerminal のドキュメントもオフィシャルにまとまってる ので、一読をおすすめします。

これで Alt + なにかのキー、で WT をサクサク切り替えることができるようになりました。

Linux での使い慣れたコマンドを使う

PS では cd や ls, cp といった MacOS / Linux で使い慣れたコマンドが実行できます。ただ ls を実行すると分かる通り、なにやら見慣れた ls とは表示が違いますね。これらはPSのコマンドレットのエイリアスなのです。

$ Get-Alias ls, cd
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           ls -> Get-ChildItem
Alias           cd -> Set-Location

使い慣れているはずのコマンドがない、挙動が違う、等々わずらしいので、ストレスにならない程度に基本的なコマンドを置き換えていきます。まずは ls や cat など基本的な物を Linux のコマンドと同じ挙動にしてしまいます。

そのためのコマンド群として、gow (Gnu On Windows)unxutils, 現在も継続的に開発が行われている busybox-w32 などがあるのですが、マルチバイト文字エンコーディングの問題*2が発生しやすく、問題なく動いていると思いきや日本語を扱うタイミングでハマりがちです。

色々と使ってみた感じ、Rust で書かれたクラスプラットフォーム CoreUtils の uutilsが PS 上でも問題なくマルチバイト文字の入力、表示が行えたのでこちらをインストールして利用します。

$ scoop install uutils-coreutils
$ uutils ls -alhF
# この出力、安心する…

ただ、実行には prefix に uutils を付ける必要があるため、uutils で提供されているコマンド、かつもともとの PS で設定されている alias が -Force で設定されてないもの(上書きすると他の動作を壊すかもしれないため。具体的には tee, sort, sleep ) 以外を PowerShell でガっと登録してしまいます。

# プロファイルに追加
@"
  arch, base32, base64, basename, cat, cksum, comm, cp, cut, date, df, dircolors, dirname,
  echo, env, expand, expr, factor, false, fmt, fold, hashsum, head, hostname, join, link, ln,
  ls, md5sum, mkdir, mktemp, more, mv, nl, nproc, od, paste, printenv, printf, ptx, pwd,
  readlink, realpath, relpath, rm, rmdir, seq, sha1sum, sha224sum, sha256sum, sha3-224sum,
  sha3-256sum, sha3-384sum, sha3-512sum, sha384sum, sha3sum, sha512sum, shake128sum,
  shake256sum, shred, shuf, sleep, sort, split, sum, sync, tac, tail, tee, test, touch, tr,
  true, truncate, tsort, unexpand, uniq, wc, whoami, yes
"@ -split ',' |
ForEach-Object { $_.trim() } |
Where-Object { ! @('tee', 'sort', 'sleep').Contains($_) } |
ForEach-Object {
    $cmd = $_
    if (Test-Path Alias:$cmd) { Remove-Item -Path Alias:$cmd }
    $fn = '$input | uutils ' + $cmd + ' $args'
    Invoke-Expression "function global:$cmd { $fn }" 
}

👆を読み込んだあと、各種コマンドを実行してみます。

$ ls -alhF
$ whoami
$ ln -s a.txt b.txt
# シンボリックが作れるユーザなら、うまく作れるはず

これで、uutils のコマンドの prefix をつけずに実行できるようになりました。なお、PSでは、Alias -> Function -> .exe などのプログラム、の優先順序で入力したコマンドが実行されるため、グローバルなFunction を作ったのに、とか実行ファイルがパスが通ったところにあるのに実行されない場合は、Get-Alias で alias がつかわれていないかなどを確認しましょう。

なお、もし uutils ls 等がうまく表示されない場合、このバイナリがコンパイルされたときに使われた Visual C++ ランタイムが入っていない可能性があります。通常 Windows を使ってると、VC++ランタイムはなんらかのソフトウェアのインストールと同時に入れられて、いつの間にか入っていることが多いのですが、入っていない場合は

から vc_redist.{アーキテクチャ}.exe を入れましょう。

他の CLI のプログラムを入れる

uutils (CureUtils )のコマンド群だけでは足りない場合、他のものも入れたり、代替となるコマンドを入れましょう。例えば…

$ scoop install gzip less tar gawk sed
$ scoop install ripgrep fd

私は grep 代わりに ripgrep に alias を貼って使っています。

Set-Alias grep rg

各種プログラミング言語を入れる

さて、実際の開発に向け、各種プログラミング言語をインストールします。ここでは、Java, Ruby, Node.js (NVS) を入れてみますが、他のプログラミング言語でもたいていは問題なく scoop でインストールできるでしょう。

Java

Java は専用の buckets が用意されて、様々なバージョンがあります。

$ scoop bucket add java
$ scoop install oraclejdk14 ojdkbuild8

等々で、オラクルのJDKやOpenJDK 等々を入れることができます。java/javac にパスが通るのはもちろんのこと、環境変数 JAVA_HOME もいい感じにセットしてくれるので便利ですね。

$ scoop which java
C:\Users\hotch\scoop\apps\oraclejdk14\current\bin\java.exe
$ echo $env:JAVA_HOME
C:\Users\hotch\scoop\apps\oraclejdk14\current
$ scoop reset ojdkbuild8
Resetting ojdkbuild8 (1.8.0.252-2.b09).
Linking ~\scoop\apps\ojdkbuild8\current => ~\scoop\apps\ojdkbuild8\1.8.0.252-2.b09
$ scoop which java
C:\Users\hotch\scoop\apps\ojdkbuild8\current\bin\java.exe
$ echo $env:JAVA_HOME
C:\Users\hotch\scoop\apps\ojdkbuild8\current

scoop reset で簡単に java/javac や JAVA_HOME を切り替えられます。

Ruby

最新版を入れるには scoop install ruby で入ります。他の古い Ruby のメジャーバージョンは versions バケットで管理されています。

$ scoop bucket add versions
$ scoop install ruby ruby24
$ scoop reset ruby24
$ ruby --version
ruby 2.4.10p364 (2020-03-31 revision 67879) [x64-mingw32]
$ scoop reset ruby
$ ruby --version
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x64-mingw32]

こんな感じで、複数バージョンがあるものは versions バケットを追加することで、最新バージョン以外のバージョンも利用できます。

ただ、この状態では手元の環境では gem install で入れた gem のプログラムのがパスが通っていなかったので、scoop install ruby を入れたときに出たメッセージ通り、ridk install で実行できる RubyInstaller2 経由で MSYS2 と MSYS2 and MINGW development toolchain (buildに必要) を入れましょう。

もう一度 gem install で、例えば rails を入れると

$ gem install rails
$ scoop which rails
C:\Users\hotch\scoop\apps\ruby\current\gems\bin\rails.bat

と、今度は Path が通った場所に rails が入り、実行できるようになりました。

Node.js

Node.js も scoop install node で入れることができますが、私は最近では Node.js 自体のバージョン切り替えが簡単にできるマルチプラットフォーム対応の NVS ( Node Version Switcher )を使っているので、scoop 経由で nvs もインストールします。

$ scoop install nvs
$ nvs add latest # 最新の Node v14 が入る
$ nvs add lts # 今なら Node v12 が入る
$ nvs link lts # 標準の node コマンドには LTS のバージョン(v12) を使う

.node-version が置いてあるディレクトリで自動で Node.js のバージョンを切り替えるには nvs auto を実行します。

$ nvs auto
$ cd nodejs-project
PATH -= C:\Users\hotch\scoop\apps\nvs\current\nodejs\node\8.9.1\x64
PATH += C:\Users\hotch\scoop\apps\nvs\current\nodejs\default
$ cd ../
PATH -= C:\Users\hotch\scoop\apps\nvs\current\nodejs\default
PATH += C:\Users\hotch\scoop\apps\nvs\current\nodejs\node\8.9.1\x64

こんな感じにディレクトリに置かれている .node-version を元に PS 上で cd する時にも適用されるようになりました。

WSL 側のプログラムを実行する

WSL には今まで触れてませんでしたが、一つだけ便利なコマンド、wsl.exe の紹介です。このコマンドを実行することで、PS 上から WSL 上のコマンドを実行できます。

$ $PSVersionTable.OS
Microsoft Windows 10.0.19041
# WSL の uname を叩く
$ wsl uname -a
Linux YUIDT 4.19.104-microsoft-standard #1 SMP Wed Feb 19 06:37:35 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$ ruby --version
ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x64-mingw32]
$ wsl ruby --version
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux-gnu]

つまるところ、「Linux 側では実行できるんだけど、Powershell (Windows) には無い!」みたいなコマンドは wsl コマンド経由で実行できます。

$ wsl sudo apt-get install sl
$ wsl sl
# PowerShell 上で Linux の sl が走る!
$ wsl zsh -c "echo {00..99}" | foreach { $_.Split(' ') } | foreach -Parallel { echo curl http://localhost/$_.jpg }
# WSL 側の zsh で 00..99 の連番をつくって、それを ' ' で split して、並列で curl を実行する例
# zsh 側でわざわざ連番を作ってる意味はないけど、パイプも簡単にできる

ちなみに、WSL2 側から Windows 側のプログラムを実行したい場合、explorer.exe のように .exe をつければたいてい実行できます。

# WSL2 側から実行
$ uname -a
Linux YUIDT 4.19.104-microsoft-standard #1 SMP Wed Feb 19 06:37:35 UTC 2020 x86_64 x86_64 x8
$ explorer.exe .
# Windows の エクスプローラで WSL 側のディレクトリが開かれる
$ ruby -e 'p RUBY_PLATFORM'
"x86_64-linux-gnu"
$ ruby.exe -e 'p RUBY_PLATFORM'
"x64-mingw32"

わりと何でも出来ちゃう感ありますね。

scoop での GUI アプリケーションのインストール

最初の方で WindowsTerminal のために extras バケットを追加したのですが、このバケットを使うと、例えば VSCode, Android Studio, Google Chrome 等々、様々な Windows アプリケーションも scoop 経由でインストールできるようになります。

この辺は scoop 経由で入れるのか、オフィシャルのインストーラ経由で入れるのか、私自身まだポリシーが定まってない状態で、メジャーなものはオフィシャルのインストール方法で入れ(大概セルフアップデートできるから)、そうでないものや自分であとから Path を通す必要があるものは scoop 経由でインストールしています。

PS 上で、どこまで操作を MacOS / Linux とあわせるか

個人的にはこれぐらいで、Windows でのコンソール(PS)上で基本的な部分での操作の不満はほとんど解消され、快適に PS Core 上で暮らせるようになりました。大抵の物が scoop でインストールでき、コマンド実行ができるように Path が通り(これの手動設定が昔は大変面倒だった…)、また基本的な操作 ( ssh や git 周り、ディレクトリ移動周り、CoreUtils 周り)も問題なく行えています。

根っからの Windows 開発者からしてみたら「いやいや、Linux のコマンド使わずに PowerShell のコマンドレットに慣れたほうが良いんじゃ」という意見ももっともで、PS 上で生活するならそのほうが生産性は上がるんだろうなぁと私も思っているので、最初は Linux と同等のコマンドを使いつつも、コマンドレットでできるならそちらを使っていきたいなぁと思ってます。PS のチュートリアルも短時間で読めますし、VSCode の PowerShell 拡張も良くできているため、PS のコードも書きやすいですしね。

逆に、Windows上でbash/zshをそのまま使うことにこだわりたかったら、MSYS2などを使ったほうが幸せそうです。

Windows を開発に使う、という選択肢

最近までは、MSプロダクトを使わない Web 開発者は、 Windows を使う選択肢が少なめだったと思うのですが、WSL2 の使いやすさも相まって、iOS 開発が必要なければ Windows (+WSL2) でバリバリ開発してもとりわけ問題を特に感じません。また Windows 側の環境を整える前までは、ほとんどのコンソール操作は WSL2 (で動いている Ubuntu 上の zsh + tmux)で行っていたのですが、環境を整えた後は Windows 上の PS Core でもだいぶ操作がしやすくなったため、Linux 上での必要性があること以外は Windows 側で行えるようになりました。

もともと MacOS や Linux を使ってると、Windows を使い始めても Linux がママ使える WSL2 な開発環境にフォーカスしてしまう(そして Windows + WSL2 はよくできているのでそれでも満足できてしまう)のですが、Windows Terminal + PowerShell Core + Scoop な Windows 環境もだいぶ使いやすいなぁと思ったので、WSL2ではない Windows 環境もより積極的に活用していけたら、と思っています。

おまけ

この記事で記載している、WindowsTerminal の設定と、user_profile.ps1 はこちら。

*1: scoop がスタートメニューにショートカットを追加しています

*2: PS では [System.Console]::OutputEncoding と[System.Console]::InputEncoding を、また Unix ライクなコマンドでは $env:LANG を適切にセットする必要があります。何も考えずに UTF-8 固定にしてしまうと、 Shift_JIS と UTF-16 の文字コードを扱うときにハマったりします。そのため、私は文字エンコーディングを指定しておらず、指定が必要な場合に状況に応じて変更しています。