改用 Mac 已經六年了。現在要再回頭碰 Windows,還真有點不習慣。

這年頭,許多軟體其實都有 Mac 及 Windows 版了:Chrome、Firefox、Slack、Evernote、Dropbox、VSCode……最大的差別,應該是終端機命令列工具。

上古時代,需要靠 CygwinMinGW 方案,才能勉強湊出一點點 Unix 的命令列感覺,但地雷超級多,難以作為嚴肅用途。後來,到了 2015 年,從保哥那邊知道有 Cmder 這個好物 1,微軟又於 2016 年推出 WSL (Windows Subsystem for Linux) 機制,Windows 這邊似乎出現曙光,對 Unix 命令列愛好者展現出久違的吸引力。

為了在 Windows 10 上面復刻我的 Mac 的體驗:iTerm2 + Zsh + Oh My ZSH,我試了幾天,把步驟整理如下。

WSL

請根據保哥的文章〈介紹好用工具:WSL (Windows Subsystem for Linux)〉進行以下步驟:

  1. 安裝 WSL。

  2. 安裝一個 Linux distribution。

  3. 第一次執行 WSL,並設定 Linux 的帳號密碼。

Zsh + Oh My ZSH

在 WSL 中安裝 zsh 及 oh-my-zsh:

% sudo apt-get install zsh

% sudo sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

將 zsh 設為內定 shell:

% chsh -s $(which zsh)

如果你的 zsh theme 含有許多特殊的符號字元,請順便安裝 Powerline 系列字型。

Cmder

Cmder 有「完整版」及「迷你版」兩種安裝方式。有了 WSL 之後,msysgit 可以退場,因此,我們只需安裝 Cmder 迷你版。

用 Chocolatey 套件管理工具安裝比較簡單:

C:\> choco install cmdermini

細節請見 https://chocolatey.org/packages/cmdermini

整合 Cmder 與 WSL

為了讓 Cmder 以 WSL + Zsh 模式啟動,我們需要新增一個 Cmder 的 “Startup / Tasks”。

譬如說,我們可新增一個名叫 {WSL::zsh} 的 task,將 command 寫成:

set "PATH=%ConEmuBaseDirShort%\wsl;%PATH%" & %ConEmuBaseDirShort%\conemu-cyg-64.exe --wsl -C~ -cur_console:p:t:"zsh" -t zsh -l
新增 Cmder 啟動設定

新增 Cmder 啟動設定

存檔完畢,以後只要以 {WSL::zsh} 模式啟動,Cmder 就會自動套用 WSL + zsh 組態。

進一步的設定細節,請參考 https://conemu.github.io/en/BashOnWindows.html 一文。

設定 Cmder 熱鍵

為了復刻出類似 Mac + iTerm2 的使用習慣,我會花一些時間調整 Cmder 熱鍵設定。請參考保哥的文章〈介紹好用工具:Cmder (具有 Linux 溫度的 Windows 命令提示字元工具)〉進行熱鍵設定。

在 Windows 與 WSL 之間進行複製貼上的剪貼簿操作時,常會遇到換行問題。此時我也會順便將 Ctrl-V 組合鍵的 “Paste mode #2” 設定成 “Multi lines” 2

調整 Cmder 剪貼簿的換行處理方式

調整 Cmder 剪貼簿的換行處理方式

檔案系統

我習慣將 Windows 的 D: 作為文件儲存專用區。

預設情況下,WSL 會將 D: 掛載在 /mnt/d,檔案系統則是 DrvFs:

% mount -l
rootfs on / type lxfs (rw,noatime)
none on /dev type tmpfs (rw,noatime,mode=755)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,noatime)
proc on /proc type proc (rw,nosuid,nodev,noexec,noatime)
devpts on /dev/pts type devpts (rw,nosuid,noexec,noatime,gid=5,mode=620)
none on /run type tmpfs (rw,nosuid,noexec,noatime,mode=755)
none on /run/lock type tmpfs (rw,nosuid,nodev,noexec,noatime)
none on /run/shm type tmpfs (rw,nosuid,nodev,noatime)
none on /run/user type tmpfs (rw,nosuid,nodev,noexec,noatime,mode=755) binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,noatime)
D: on /mnt/d type drvfs (rw,noatime,uid=1000,gid=1000,case=off)
C: on /mnt/c type drvfs (rw,noatime,uid=1000,gid=1000,case=off)

不過,因為某些複雜的原因 3,我們必須重新設定 /mnt/d 的 mount 參數,才能讓 Linux 的檔案讀寫權限正常運作。

我們先卸載 /mnt/d,再用夾帶 metadata 的方式重新掛載它:

% sudo umount /mnt/d

% sudo mount -t drvfs D: /mnt/d -o metadata,uid=1000,gid=1000,umask=22,fmask=111

先查看是否成功:

% mount -l
rootfs on / type lxfs (rw,noatime)
none on /dev type tmpfs (rw,noatime,mode=755)
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,noatime)
proc on /proc type proc (rw,nosuid,nodev,noexec,noatime)
devpts on /dev/pts type devpts (rw,nosuid,noexec,noatime,gid=5,mode=620)
none on /run type tmpfs (rw,nosuid,noexec,noatime,mode=755)
none on /run/lock type tmpfs (rw,nosuid,nodev,noexec,noatime)
none on /run/shm type tmpfs (rw,nosuid,nodev,noatime)
none on /run/user type tmpfs (rw,nosuid,nodev,noexec,noatime,mode=755) binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,noatime)
D: on /mnt/d type drvfs (rw,relatime,uid=1000,gid=1000,umask=22,fmask=111,metadata,case=off)
C: on /mnt/c type drvfs (rw,noatime,uid=1000,gid=1000,case=off)

如果成功了,請記得將這設定寫進 /etc/fstab 裡面,下次 WSL 啟動時就會自動生效:

D: /mnt/d drvfs rw,relatime,uid=1000,gid=1000,metadata,umask=22,fmask=111 0 0

Docker 與 Kubernetes

雖然保哥的文章推薦在 WSL 裡的 Linux 再安裝一份 Docker engine 4,但我比較傾向不要。我比較傾向共用既有資源,讓 WSL 直接連接到 Docker Desktop for Windows 身上。

請先根據上官林傑的文章〈在 Windows Subsystem for Linux (WSL) 下使用 Windows 上的 Docker Engine〉,做好必要的設定,但請先省略其中的 alias 步驟(稍後會說明為什麼):

  1. 打開 Docker Desktop for Windows 的 “Expose daemon on tcp://localhost:2375 without TLS.” 選項。

  2. 在 WSL 裡設定 DOCKER_HOST 環境變數:
    export DOCKER_HOST="tcp://localhost:2375"

  3. 在 WSL 裡 sudo visudo 以下環境變數:
    Defaults env_keep += "DOCKER_HOST"

上述文章建議透過 alias docker=docker.exe 的方式來使用 Docker Desktop for Windows 的 docker client。不過,這些 alias 未必都能在 script 中展開 5 ——有鬆綁的方法,但我不喜歡破例。我比較傾向在 WSL 裡建立符號連結:

% sudo ln -s "/mnt/c/Program Files/Docker/Docker/resources/bin/docker.exe" \
    /usr/local/bin/docker

% sudo ln -s "/mnt/c/Program Files/Docker/Docker/resources/bin/docker-compose.exe" \
    /usr/local/bin/docker-compose

% sudo ln -s "/mnt/c/Program Files/Docker/Docker/resources/bin/kubectl.exe" \
    /usr/local/bin/kubectl               

也別忘了,要將 Windows 上面的 Kubernetes 設定連接過來:

% ln -s /mnt/c/Users/xxx/.kube/config ~/.kube/config



好了,現在已經復刻一部分 Mac 的使用習慣了。是該要好好享受一下這種混血環境了。

2019-04-17 後續發展

使用了一個月,不太能夠忍受 Cmder 不夠穩定的顯示邏輯:在視窗尺寸變化時,無法正確處理字元位置。因此,我改用〈WSLtty + tmux 組合技〉。


  1. 保哥於 2015 年九月舉辦過【打造一個具有 Linux 溫度的 Windows 命令提示字元工具】線上講座,也寫了〈介紹好用工具:Cmder〉一文。 [return]
  2. Cmder 的剪貼簿設定細節,請見 https://conemu.github.io/en/SettingsPaste.html[return]
  3. 關於 WSL DrvFs 的 metadata 權限設定細節,請參考這三篇文章:“Chmod/Chown WSL Improvements”、“Going Overboard with WSL metadata”、〈WSL 配置指北:打造 Windows 最强命令行〉。 [return]
  4. 保哥的文章〈我的 Windows Subsystem for Linux (WSL) 終極開發人員配置 - 2018 版〉。 [return]
  5. Bash 的行為是:“Aliases are not expanded when the shell is not interactive, unless…”,請參考 “Why doesn’t my Bash script recognize aliases?” 這類文章。 [return]