微信号:jishuqushi

介绍:除了技术干货,这里啥也没有了.

炫酷神器 TheFuck 是如何工作的?听作者讲述实现原理

2017-05-17 07:54 技术风向标
在昨天的文章 自从用上命令行神器「thefuck」, 再也不用担心敲错命令了 中,小标为大家介绍了一款居家旅行必备神器——The Fuck,由于它率性的名字和炫酷的功能而备受关注。如果你对它的实现原理感兴趣,不要错过今天的续集。它的开发者 Vladimir Iakovlev 其实早在 2015 年就在 GitHub 上分享过。



不久前,我写了一款实用的应用 —— The Fuck,用于修复命令行中上一条错误的命令。它被下载了上万次,在 GitHub 上有很多 star,还有几十个优秀的贡献者。并且,它的内部实现是很有意思的。


管道

简而言之,可以把TheFuck理解成一个管道,从用户的角度看来就像这样:

哪里搞错了 -> Fuck -> 搞定

之所以这么简单,是因为 fuck 只是一个别名(alias)而已。它会对错误的命令做一些处理,然后执行它修正后的命令并更新命令执行历史。比如对 zsh 是这样的:


TF_ALIAS=fuck alias fuck='eval $(thefuck $(fc -ln -1 | tail -n 1)); fc -R'

继续说回管道,在这个别名内部,thefuck是这样工作的:


出错的命令 -> thefuck -> 修正后的命令


有意思的部分都在上面这个 thefuck 里面:


出错的命令 -> 规则匹配 -> 多个修正后的命令 -> 用户选择一个 -> 修改好的命令


这里最重要的部分就是规则匹配,规则是个有两个方法的特殊模块集:


  • match(command: Command) -> bool – 当规则匹配上时返回true;

  • get_new_command(command: Command) -> str|list[str] – 返回修正后的命令或命令列表;


我觉得这个应用酷就酷在有这些规则,编写自己的规则也很简单。目前有75条可用的规则,大都是由第三方贡献者写的。命令是一个类似命名元组(namedtuple)这样的数据结构:


Command(script: str, stdout: str, stderr: str)


其中script就是错误命令,是与shell类型无关的


处理不同的Shell

在各种shell中,对alias 的描述方式不同、语法不同(比如在 fish 中 用and表示&&)、历史命令的处理方法也不同。而且 shell 还依赖特定的配置文件(.bashre ,. zshrc 等)。


为了避免这些麻烦,在程序中有一个 shells 模块,把这些与特定 shell 相关的命令转化为与 sh 兼容的类型,并展开别名和环境变量。所以我们用shells.from_shell 方法来获得 Command 的实例,在 sh 里运行获得stdout和stderr。


出错的命令 -> from_shell -> 与 shell 类型无关的命令 -> 在 sh 内运行 –> Command实例


对修正后的命令也做相应处理,也就是把 shell 类型无关的命令通过 shells.to_shell 转化为该 shell 对应的命令。


配置


TheFuck 是一款配置灵活的应用,用户可以开启或关闭规则、配置 UI、设置规则选项等等。可以通过修改 ~/.thefuck/settring.py 文件以及环境变量来配置应用:


默认配置 -> 修改 setting.py 文件 -> 通过环境变量更新


在之前的版本中,配置对象以参数的形式传递到所有需要的场合,虽然那样也还行,但是代码重复过多。而现在是一个单例(thefuck.conf.settings),类似Django中的django.conf.settings。


UI

TheFuck 的UI很简单,它允许用户通过方向键在修正后的命令列表中选择,用 Enter 键确认选择,Ctrl+C 跳出程序。 不足的是在 Python 标准库中没有办法在非Windows下不通过 curses 来读取键盘输入,由于别名(alias)的特性我们又不能在这里使用 curses。不过针对Windows的 msvrt.getch 也很容易:

import ttyimport termiosdef getch():    fd = sys.stdin.fileno()    old = termios.tcgetattr(fd)    try:        tty.setraw(fd)        ch = sys.stdin.read(1)        if ch == '\x03':  # For compatibility with msvcrt.getch            raise KeyboardInterrupt        return ch    finally:        termios.tcsetattr(fd, termios.TCSADRAIN, old)

另外UI也需要修正后的程序命令组成的有序列表,且规则匹配耗时应该尽量较短。而加入简单的启发式算法后效果还不错,首先我们按照优先级来匹配规则,第一个返回的命令是有最大优先级的命令,当用户按下方向键时再选择其他的命令。所以在大多数使用场景中都能很快完成任务。


整体

总的来看,这款应用很简单:


其中 controller是当用户使用 thefuck 来修复错误命令时的程序入口,它初始化设置、准备 shells 的交互环境、从 Corrector 获取修正过的命令并在 UI 中选择。


Corrector 使用所有可用的规则来匹配当前命令,并返回所有可用的修正后的命令。


关于UI、设置和规则就说到这里。


测试

测试是所有软件项目最重要的部分之一。没有测试,软件可能会由于一个改动而崩溃。我们用 pytest 来做单元测试。


由于应用中规则的存在,需要做很多测试来匹配和确认修正后的命令。所以把测试用例参数化很有必要,典型的测试是这样的:


import pytest
from thefuck.rules.cd_mkdir
import match, get_new_command
from tests.utils import Command

@pytest.mark.parametrize('command', [    Command(script='cd foo', stderr='cd: foo: No such file or directory'),    Command(script='cd foo/bar/baz',            stderr='cd: foo: No such file or directory'),    Command(script='cd foo/bar/baz', stderr='cd: can\'t cd to foo/bar/baz')])
def test_match(command):    assert match(command)


TheFuck 兼容多种 shell ,而每个 shell 又需要特定的别名,为了保证所有别名可用,需要功能测试。用到了我写的 pytest-docker-pexpect 模块,它在一个 docker 容器内设置一个场景来测试所有支持的命令。


发布

TheFuck 最麻烦的部分是安装,它通过pip发布,碰到了一些问题:


  • 有些平台上依赖python的头文件(python-dev),所以用户需要手动安装;

  • pip不支持安装后的hook操作,所以用户需要手动配置别名;

  • 有些用户用的是不支持的python版本,thefuck目前只支持2.7或3.3+的版本;

  • 有些老版本的pip根本就不安装依赖项;

  • 有些版本的pip无视Python版本的依赖关系,所以早于3.4的版本要安装pathlib;

  • 有趣的是有人讨厌这个名字所以想从pypi中移除这个包;


大部分问题都可以通过专门的install脚本解决,该脚本内部使用了pip,但会在安装前对系统进行一些准备工作,并在安装后配置别名。


近期文章

 

SQL诞生已43年,人们仍在使用它的8个原因

自从用上命令行神器「thefuck」, 再也不用担心敲错命令了

Facebook开源Prepack,自动优化JavaScript代码

微软发布 Visual Studio 2017 for Mac 正式版

Google 工程师带你入门 Headless Chrome

Google新手机OS Fuchsia有崭新UI,抛弃Linux

现如今学 JavaScript 是一种什么样的体验?

2017 年Web开发者技能学习路线图


微信公众号"技术风向标",关注IT趋势,承载前沿、深入、有温度的内容。长按下方二维码加关注。

 
技术风向标 更多文章 自从用上命令行神器「thefuck」, 再也不用担心我敲错命令了 勒索病毒大范围蔓延,源自美国安局被盗的黑客工具 微软发布 Visual Studio 2017 for Mac 正式 Google新的手机操作系统Fuchsia具有崭新UI,抛弃Linux 2017 年Web开发者技能学习路线图
猜您喜欢 微信终端跨平台组件 mars 系列(一) - 高性能日志模块xlog 你真的了解View的坐标吗? 谈谈分类器的损失函数(2) 【冬瓜哥归来】传统存储老矣,新兴存储能当大任否? Hive的那些事