WSL 配置指北:打造 Windows 最强命令行

原文发表在个人 博客 上,欢迎订阅。;)

在两年前的八月,Microsoft 正式发布了 Windows 10 Anniversary Update 周年更新(它还有着 RS1,Version 1607,Build 14393 等一大堆别名)。其中最让包括我在内的众多开发者感到兴奋的特性之一,就是 WSL(Windows Subsystem for Linux,当时还叫 Bash on Ubuntu on Windows)的正式加入。php

在 Windows 上原生运行 Linux 可执行文件,牛逼疯了!node

然而 Bug10 也不是浪得虚名,本来只提供给 Insider 的 WSL 在正式发布后依然问题多多(不只 zsh、tmux 等工具没法使用,网络相关的操做更是一律欠奉,还有各类各样 奇妙的 BUG),基本没有可用性,我在尝鲜了一段时间后也不得不重回 Cygwin 的怀抱。不过好消息是,在以后的更新中,这些 BUG 都已被逐一消灭linux

通过了两年的发展,WSL 已经足够成熟,我也是时候完成这篇一咕再咕的博文了。git

get-wsl

1. 我理想中的命令行界面

既然违反广告法取了这么个标题,那我天然得先描述一下个人目标,也就是我理想中的命令行界面应该是什么样子的(若是你不清楚命令行的概念,能够看看我以前写的 这篇文章):github

  • 好看(配色、字体能够自由设定);
  • 支持 UTF-8 字符的输入与显示;
  • 支持常见的 *NIX 命令行工具(cat、grep、awk 等);
  • 自动补全、语法高亮、历史记录;
  • 完善的复制粘贴支持;
  • 互操做性(共享文件系统、网络栈,可调用 Win32 程序);
  • 支持经常使用的脚本语言(PHP、Python、Node.js 等);
  • 包管理器,以及其余各类经常使用软件的支持;
  • 快速呼出(快捷键、右键菜单入口)。

然而遗憾的是,Windows 上的命令行一直以来都很微妙。web

2. 难用的 Windows 命令行

停停停,那边的 PowerShell 爱好者 ,咱别动粗成吗?shell

首先我要对标题作出一些订正,Windows 原生命令行其实也能够不那么难用。虽然 cmd.exe 是公认的难用到反人类(毕竟是用来兼容 DOS 的老古董),但后来推出的 PowerShell 已经足够强大且现代化,可以称得上是一个成熟的命令行 Shell 了。若是你愿意学习的话,PowerShell 几乎能够知足你对命令行的全部期待。这一点能够参见:Is PowerShell ready to replace my Cygwin shell on Windows?ubuntu

可是,PowerShell 与 Bash 等类 Unix 系统上的 Shell 程序几乎是两个彻底不一样的世界。不只语法不一样,其平台上各种经常使用的命令行工具也基本不一致(好比类 Unix 系统中的 grep 对应 PowerShell 中的 Select-Stringuniq 对应 Select-Object -Unique 等)。往深了说,他们的系统设计理念都是不同的,好比不少人推崇的 Unix 哲学,在 Windows 上就基本不见踪迹;而 COM 等概念也是 Windows 独一份。windows

manga-system-admin-girl-sp-wsl

▲ 图片来源:シス管系女子 BEGINS 特別編 まんがでわかる WSL浏览器

固然,我无心在此挑起操做系统间的圣战。Windows 和类 Unix 系统中的命令行哪一个好用,见仁见智。不过对于包括我在内的不少用户都认为 Windows 命令行不怎么好用,仅此而已。

回到正题。

虽然 Windows 的命令行一直遭人诟病,可是人家的图形界面牛逼啊。因而无数工程师前赴后继,试图在 Windows 上创造出不输给类 Unix 系统的命令行体验 —— 却绝大多数以失败了结。曾经努力过的人,或者回到可爱的 Linux 上,或者进入高贵冷艳的 macOS 的世界。其中有先辈留下了 Cygwin、GnuWin32 等工具集,让咱们能够在 Windows 下使用类 Unix 系统中常见的命令行工具,成为了避免少 Windows 用户的救赎。

然而,就当你们都以为「也就这样了」的时候,Microsoft 出人意料地站了出来。

带着他新鲜出炉的 WSL。

3. Windows Subsystem for Linux,参上!

你们都把 WSL 吹得这么牛逼,那 WSL 到底是个什么玩意儿呢?

简单来讲,WSL 是一个 兼容层,有点像反过来的 Wine。


首先,我问个问题,为何 Linux 上的程序没法在 Windows 上运行呢?

了解过一点操做系统原理的同窗应该都知道,这是 Windows 与 Linux 的内核提供的接口不一样(系统调用、API 等)致使的。举个栗子,咱们想知道某目录下的内容,在 Linux 下咱们会使用 ls 命令,而在 Windows 下咱们会使用 dir 命令。

当咱们在 Linux 上执行 ls 命令,ls 会调用 getdents 这个系统调用,Linux 内核收到请求,将目录的内容返回给应用程序;当咱们在 Windows 上执行 dir 命令,dir 会调用 NtQueryDirectoryFile 这个 API,NT 内核收到请求,将目录的内容返回给应用程序。虽然系统不一样,但基本上都是一个道理。

然而,当咱们把 Linux 上的应用程序拿到 Windows 上运行时,应用程序和内核就双双懵逼了。好比 ls 会尝试调用 getdents 系统调用(理想化的状况下,暂不考虑可执行文件格式等问题),Windows 的 NT 内核一看,心说:「这他娘的什么东西,老子不认识啊,啥状况啊」,ls 也想:「尼玛,内核怎么不回话啊,咋回事儿啊」……两边语言不通,应用程序天然没法正确执行。

可是有了 WSL,状况就不同了。

依然拿 ls 举例,当咱们在 WSL 中运行 ls 命令时,ls 会调用 getdents 系统调用(这个系统调用接口是 WSL 提供的,Windows 自己并无这个接口),WSL 收到这个请求,明白了应用程序是想要知道目录的内容,因而把 Linux 的系统调用转换为 NT API NtQueryDirectoryFile。NT 内核收到 WSL 的请求,将目录的内容返回给 WSL,WSL 再把返回的内容包装好后返回给 ls

也就是说,WSL 在 Linux 应用程序与 Windows NT 内核之间起到了翻译者的做用。很简单的道理,既然 NT 内核没法理解 Linux 应用程序的 POSIX 系统调用,那就弄个翻译来将 POSIX 系统调用实时转换为 NT 内核能理解的 API 调用,突出一个见人说人话、见鬼说鬼话。

只要实现了足够多的系统调用翻译,那么理论上 WSL 能够彻底模拟成一个 Linux 内核。


相信各位都据说过鼎鼎大名的 Cygwin。一样是能让 Linux 应用程序运行在 Windows 上,WSL 和 Cygwin 有什么不一样呢?其实差异仍是挺大的。

虽然 Cygwin 提供了完整的 POSIX 系统调用 API(以运行库 Cygwin*.dll 的形式提供),但其依然工做在 User Mode;而 WSL 中的 Linux 应用程序进程会被包裹在一个叫作 Pico Process 的东西里,这个东西里发出的全部系统调用请求都会被直接送往 Kernel Mode 中的 lxcore.syslxss.sys 处理。

一样是将 POSIX 系统调用转换为 Windows 中的 API,Cygwin 是转换成 Win32 API 的调用(由于它架设在 Win32 子系统上,不少内核操做受限于 Win32 的实现,好比 fork),而 WSL 则是转换为更底层的 NT API 调用(WSL 是与 Win32 平行的子系统,直接架设在 NT 内核上,能够经过 NT API 原生实现 fork 等系统调用)。

wsl-architecture

▲ WSL 架构示意图。图片来源:Windows for Linux Nerds

最重要的一点:若是使用 Cygwin,Linux 应用程序的源码必须 link 至 Cygwin 运行库(Cygwin*.dll),修改源码从新编译后才能在 Windows 下运行。这些从新编译后的 Linux 应用程序在调用 POSIX API 时不会直接去请求内核,而是会去调用 Cygwin 运行库,由运行库翻译成 Win32 API、执行调用后返回结果。这也就意味着,从新编译后的应用程序须要依赖 Cygwin 运行库才能正常运行(有时候你会碰到的「缺乏 Cygwin1.dll」报错就是这个缘由),并且这样编译出来的可执行程序是纯正的 Win32 PE 格式封装,只能在 Windows 上运行。

而在 WSL 下,咱们能够直接运行未经任何修改的 ELF 格式 Linux 可执行程序。

ls-exe-in-cygwin

▲ Cygwin 目录下,被编译成 Win32 可执行程序的 Linux 应用程序们。

最后总结一波:

WSL 就像是一个翻译官,就算那些未经修改的 Linux 应用程序们操着一口纯正的 POSIX 系统调用语法,WSL 也能快速准确地将其翻译为 NT 内核能听懂的 API 调用;

而那些使用了 Cygwin 从新编译后的 Linux 应用程序,就像是改造人同样变成了 Win32 应用程序的形状,还被套了个翻译机。程序本身(源码中)说的是 POSIX,通过翻译机(Cygwin 运行库)以后就变成 Win32 API 调用了,这样 NT 内核也能听得懂。

可是每次添加新程序都要改造,多麻烦啊,仍是 WSL 原生态更健康(笑)。


以上只是我对 WSL 的粗浅解释,其具体实现原理能够参考官方博客上的 这一系列文章

4. 安装 WSL,拥抱可爱的 Linux

好了不说废话,让咱们开始安装 WSL。注意,WSL 仅支持 64 位系统,且本文中所描述的安装方法仅适用于 Windows 10 Fall Creators Update(秋季创意者更新,RS3,Version 1709,Build 16299)及以上版本。

第一步,打开「控制面板」中的「程序与功能」,点击左侧边栏的「启用或关闭 Windows 功能」选项,在弹出的窗口中勾选「适用于 Linux 的 Windows 子系统」,而后点击肯定(可能须要重启)。

若是你懒得用 GUI,也能够直接在 PowerShell 中以管理员权限执行命令:

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

enable-windows-optional-feature

第二步,打开 Microsoft Store,搜索「WSL」。挑选一个你喜欢的 Linux 发行版,而后点击安装。(截至目前,商店中可用的发行版有 Ubuntu、openSUSE、SUSE Linux Enterprise Server、Debian 以及 Kali Linux。)

microsoft-store-wsl

第三步,在开始菜单中找到你刚刚安装的发行版,打开它。等待几分钟的初始化过程,设定好用户名与密码后(不须要与 Windows 的相同,用过 Linux 的选手应该都懂的)就会自动进入 Linux 环境。

至此,你已经完成了 WSL 的安装。

你也能够同时安装多个发行版,它们的数据都是独立的,互不影响。

initialize-wsl

5. 使用更专业的终端模拟器

我猜你如今正在对上面那个窗口发呆。

—— 这个新宋体他娘的是个什么状况?

若是你正在使用中文 Windows 系统,并且以前并无修改过 Win32 Console 的默认配置,那么你的 WSL 终端默认就会是这样的。新宋体,就是这么 Hardcore。惊不惊喜,意不意外?

好吧不开玩笑,Windows 这个控制台窗口就是不少人讨厌它的缘由之一,难用又难看。丑这一点倒还有解决方法(通过一番设置后还算能看,我之前就写过一篇关于 自定义 Windows 控制台字体 的文章),难用倒是实打实的。尽管 Win10 上的控制台已经改进了很多(能够看看 Microsoft 的官方博客:Windows Command Line Tools For Developers),但其依然是最难用的终端模拟器之一,或许没有之一。

所以,为了实现咱们的目标,一个更强大的终端模拟器是必须的。

终端模拟器是什么?为了这个回答这个问题,我专门写了一篇文章, 去看看吧。:P

我我的比较推荐的终端模拟器有:

  • wsl-terminal

    专门为 WSL 开发的终端模拟器,基于 mintty 与 wslbridge,稳定易用。

  • ConEmu

    Windows 上的老牌终端模拟器,功能极为强大,要啥有啥。

  • Hyper

    基于 Electron 的跨平台终端模拟器,好看和可扩展性是卖点,BUG 很多

还有其余各类各样的终端模拟器,选个本身喜欢的就好。反正无论选哪一个,都比默认的那玩意儿要好用。🌚

另外,设定终端模拟器的 Shell 入口时有个坑,须要注意一下(参见下文 6.4)。

my-terminals

▲ 我正在使用的终端,wsl-terminal 与 Hyper。好看是第一辈子产力。

6. 让咱们更深刻一些

如下是 WSL 的一些优化技巧。

6.1 使用软件源镜像

因为众所周知的缘由,各大发行版默认的软件源在中国大陆的访问速度都很屎。

我目前使用的是 清华大学的 Ubuntu 镜像源

6.2 安装 zsh 与 oh-my-zsh

想要快乐地使用命令行,一个趁手的 Shell 是必不可少的。

我我的习惯使用 zsh,安装步骤再也不赘述。个人自定义 oh-my-zsh 主题

# ~/.oh-my-zsh/custom/themes/robbyrussell-ascii.zsh-theme
# Modified from robbyrussell, the default theme of oh-my-zsh.
# > blog git:(source) x $

local ret_status="%(?:%{$fg_bold[green]%}>:%{$fg_bold[red]%}>%s)"
PROMPT='${ret_status} %{$fg[cyan]%}%c%{$reset_color%} $(git_prompt_info)$ '

ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg_bold[blue]%}git:(%{$fg[red]%}"
ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%} "
ZSH_THEME_GIT_PROMPT_DIRTY="%{$fg[blue]%}) %{$fg[yellow]%}x%{$reset_color%}"
ZSH_THEME_GIT_PROMPT_CLEAN="%{$fg[blue]%})"
# ~/.zshrc
ZSH_THEME="robbyrussell-ascii"
plugins=(git zsh-completions zsh-autosuggestions zsh-syntax-highlighting)

6.3 安装多个发行版

Windows 10 Fall Creators Update 以后,WSL 支持同时安装多个 Linux 发行版,直接在 Microsoft Store 中搜索想要的发行版并点击安装便可。这些发行版能够同时运行,而且数据互相独立。你可使用 wslconfig.exe 来查询已安装的发行版,或者更改默认的发行版。

multiple-wsl-distributions

删除发行版也很简单,直接卸载对应的商店应用便可(记得备份哦)。

6.4 多种进入 WSL 的方式比较

新版支持同时安装多个发行版,那天然不能像之前那样只提供一个 bash.exe 入口了。

秋季创意者更新以后的 Windows 提供了 多种进入 WSL 环境的方式

  • wsl.exe

    打开默认发行版中的默认 Shell。

  • <distroname>.exe

    打开指定发行版中的默认 Shell。

  • bash.exe (DEPRECATED)

    打开默认发行版中的 bash Shell。

    若是你更改了默认 Shell 却老是打开 bash,就说明你使用了这个入口。

你也能够经过这些入口直接在 WSL 中执行命令并返回结果:

  • <distroname> -c [command]
  • bash -c [command]
  • wsl [command](再也不须要指定 -c

invoking-wsl-in-many-ways

6.5 与 Windows 的互操做性

WSL 与 Windows 之间的互操做性 (Interoperability) 很牛逼。怎么个牛逼法呢?

Windows 下的全部盘符都挂载在 WSL 中的 /mnt 目录下,能够直接操做。WSL 中的全部数据则存放于 C:\Users\{你的用户名}\AppData\Local\Packages\{Linux发行版包名}\LocalState\rootfs 目录中(不要在 Windows 中修改这些文件,这会形成文件权限错误):

$ ls /mnt
c  d  e
$ mount -l
rootfs on / type lxfs (rw,noatime)
C: on /mnt/c type drvfs (rw,noatime,uid=1000,gid=1000)
D: on /mnt/d type drvfs (rw,noatime,uid=1000,gid=1000)
E: on /mnt/e type drvfs (rw,noatime,uid=1000,gid=1000)

你能够在 Windows 命令行环境中直接调用 WSL 中的命令:

PS C:\temp> wsl ls -al
total 0
drwxrwxrwx 1 printempw printempw 4096 Sep  7 19:04 .
drwxrwxrwx 1 printempw printempw 4096 Sep  7 18:38 ..
-rwxrwxrwx 1 printempw printempw    4 Sep  7 19:04 foo.txt

你也能够在 WSL 中调用 Windows 中的命令行程序:

$ which ipconfig.exe
/mnt/c/Windows/System32/ipconfig.exe
$ ipconfig.exe
Windows IP Configuration
...

你能够在 WSL 中直接启动 Windows 应用:

$ notepad.exe "C:\temp\foo.txt"

你还能够经过 pipes 与 Windows 程序通讯:

# 复制内容至 Windows 剪贴板
$ cat foo.txt | clip.exe

你甚至能够把 Windows 命令和 WSL 命令混着用:

PS> ipconfig | wsl grep IPv4
IPv4 Address. . . . . . . . . . . : 192.168.1.114

$ ipconfig.exe | grep IPv4 | cut -d: -f2
192.168.1.114

$ ls -al | findstr.exe foo.txt
-rwxrwxrwx 1 printempw printempw    4 Sep  7 19:04 foo.txt

$ cmd.exe /c dir
 Volume in drive C is Windows
 Volume Serial Number is B263-****

 Directory of C:\temp

2018/09/07  19:04    <DIR>          .
2018/09/07  19:04    <DIR>          ..
2018/09/07  19:04                 4 foo.txt
               1 File(s)              4 bytes
               2 Dir(s)  194,422,341,632 bytes free

同时,WSL 与 Windows 共享网络栈,也就是说你能够:

  • 在 WSL 中启动 web server,在 Windows 上使用浏览器访问;
  • 在 Windows 下启动 MySQL/Redis 服务器,在 WSL 中链接;
  • 诸如此类。

若是你对 WSL 与 Windows 之间互操做的原理有兴趣,能够参考一下这些文章:

6.6 DrvFs 文件权限问题

虽然 WSL 中能够直接访问 Windows 磁盘的内容,但若是你曾经这么作过,你应该对这样绿油油一片的 ls 不会感到陌生。为何 NTFS 文件系统中的文件到 WSL 下权限就所有成 0777 了呢?

ls-with-wrong-file-permission

这主要是 DrvFs 中 Linux 文件权限的实现致使的。

在 WSL 中,Microsoft 实现了两种文件系统,用于支持不一样的使用场景:

  • VolFs

    着力于在 Windows 文件系统上提供完整的 Linux 文件系统特性,经过各类手段实现了对 Inodes、Directory entries、File objects、File descriptors、Special file types 的支持。好比为了支持 Windows 上没有的 Inodes,VolFs 会把文件权限等信息保存在文件的 NTFS Extended Attributes 中。记得我上面警告过你不要在 Windows 中修改 WSL 里的文件吗?就是由于 Windows 中新建的文件缺乏这个扩展参数,VolFs 没法正确获取该文件的 metadata,并且有些 Windows 上的编辑器会在保存时抹掉这些附加参数。

    WSL 中的 / 使用的就是 VolFs 文件系统。

  • DrvFs

    着力于提供与 Windows 文件系统的互操做性。与 VolFs 不一样,为了提供最大的互操做性,DrvFs 不会在文件的 NTFS Extended Attributes 中储存附加信息,而是从 Windows 的文件权限(Access Control Lists,就是你右键文件 > 属性 > 安全选项卡中的那些权限配置)推断出该文件对应的的 Linux 文件权限。

    全部 Windows 盘符挂载至 WSL 下的 /mnt 时都是使用的 DrvFs 文件系统。

因为 DrvFs 的文件权限继承机制很微妙,最后致使的结果就是全部文件的权限都变成了 0777。并且因为早期的 DrvFs 不支持 metadata,因此你没法给这些文件 chown/chmod,只能对着绿油油的 ls 干瞪眼。不过好消息是,Windows Insider Build 17063 以后,DrvFs 也像 VolFs 同样支持给文件写入 metadata 了

要启用 DrvFs 的 metadata 支持,你须要添加参数从新挂载磁盘:

# 修改为你本身的盘符
$ sudo umount /mnt/e
$ sudo mount -t drvfs E: /mnt/e -o metadata

不过若是仅仅是执行了这个,虽然支持了文件权限的修改,但磁盘下的文件权限默认依然仍是 0777,除非你给它们整个 chmod 一遍。若是你不想这么作,也能够指定其余的 mount 参数:

$ sudo mount -t drvfs E: /mnt/e -o metadata,uid=1000,gid=1000,umask=22,fmask=111

这样磁盘下的文件的默认权限就是 0644ls 也不会再是绿油油一片啦。

ls-with-fixed-file-permission

不过每次使用时都要从新挂载未免也太烦,咱们能够经过另外一个新特性 Automatically Configuring WSL 实现自动挂载。在 WSL 中建立 /etc/wsl.conf,在其中填写以下内容:

[automount]
enabled = true
root = /mnt/
options = "metadata,umask=22,fmask=111"
mountFsTab = true

# 这个文件里还能够添加其余配置项,有兴趣的能够看看上面的连接

重启终端,全部的盘符就会使用上面的配置自动挂载啦(可使用 mount -l 查看)。

另外,若是你想要给不一样的盘符设定不一样的挂载参数(上面的方法对全部盘符都有效,若是你想在 WSL 中运行 Windows 下的应用程序,就得每次都 chmod +x 一下,因此我通常都会把 C: 排除掉),就须要手动修改 /etc/fstab。首先确保 wsl.conf 中的 mountFsTabtrue,而后编辑 /etc/fstab,添加以下内容:

# 不在此列表中的盘符会使用 wsl.conf 中的参数挂载
# 格式能够本身去查 fstab 的帮助文档
E: /mnt/e drvfs rw,relatime,uid=1000,gid=1000,metadata,umask=22,fmask=111 0 0

6.7 其余关于 WSL 的折腾

虽然 Microsoft 开发 WSL 出来主要是着重于命令行环境的使用,但通过测试,WSL 是能够经过 X Server 执行 GUI 应用程序的,甚至还能够在 WSL 里面用 Wine 执行 Windows 程序……(🤔?)

也有人试过在 WSL 中运行完整的 DE,体验彷佛还不错,有兴趣的同窗能够去试试。

另外,你也能够经过某些神秘的方法用上 Microsoft Store 未提供的 Linux 发行版,好比 Arch Linux

若是你对 WSL 的底层实现有兴趣,也能够去围观一下 WSL 的官方博客:

7. 总结

虽然 WSL 很不错,可是其比起真正的 Linux 系统仍是有不少不足(Docker 等涉及未实现的内核特性的软件没法使用,Raw socket 相关的操做依然容易出错,I/O 性能相比之下较为孱弱等)。若是你平常开发中须要使用到那些 WSL 未提供的 Linux 特性,那么仍是乖乖跑 VM 或者装 Linux 吧。

对我来讲,WSL 最大的意义就是,让我可以用我熟悉的 Linux 那一套去操做 Windows

若是你和个人需求同样,那么比起 Cygwin、VM 等解决方案,WSL 有着完整的 Linux 环境、强大的互操做性、更低的资源占用。离不开 Windows,却又羡慕 Linux 下强大命令行工具的各位,相信大家会喜欢 WSL 的。

并且最近几年 Microsoft 在笼络开发者方面的努力你们有目共睹,这里就容我夸上一句:

Microsoft,干得漂亮!

相关文章
相关标签/搜索