热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

那个极爽的命令行纠错软件

前两天我们为大家介绍了一款命令行神器:TheFuck,今天让我们来看看该软件的作者是如何设计该软件的。不久之前我写了一款实用的应用— TheFuck,用来修复在命令行中上一条错误的

前两天我们为大家介绍了一款命令行神器: The Fuck,今天让我们来看看该软件的作者是如何设计该软件的。

不久之前我写了一款实用的应用 — The Fuck,用来修复在命令行中上一条错误的命令。这款应用下载了几千次,在 GitHub 上有很多 star,并有几十个优秀的贡献者。本文介绍应用中有趣的内部实现。

另外,大约一周前我谈论过《开源软件架构》一书,现在我觉得要是能在书中写一章关于 The Fuck 的内容将是很酷的事情。

管道

The Fuck 可以简单理解成一个管道,从用户的角度来看,流程如下:

有些东西出错了 → The Fuck → “完事”

之所以这么简单是因为 The Fuck 只是一个别名alias罢了(用户也可以使用其他别名)。对错误的命令进行了一些处理,执行修改过了的命令并更新命令历史。比如对于 zsh 是这样的:

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

让我们再回到管道上来,对于在别名alias内运行的的 The Fuck 来说,管道是这样的:

出错的命令 → The Fuck → 修复好的命令

所有有趣的事都发生在 The Fuck 当中:

错误的命令 → 匹配规则 → 修正后的命令 → 用户选择 → 修改好的命令

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

  • 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 相关的命令。

配置

The Fuck 是一个高可配置的应用,用户可以开启或关闭规则、配置 UI、设置规则选项还有进行其他的操作。用户可以通过修改 ~/.thefuck/settring.py 文件以及环境变量来配置应用:

默认配置 → 通过 setting.py 文件更新 → 通过环境变量更新

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

UI

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

import tty
import termios

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

整体来看

如果从整体来看一下这个应用,会发现它很简单:

那个极爽的命令行纠错软件 The Fuck 是如何工作的

其中 controller(控制器)是当用户使用 The Fuck 来修复错误命令时的程序入口,它初始化设置、准备 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)

The Fuck 可以与许多种类的 shell 共同工作,而每个 shell 又需要特定的别名。为了保证所有别名可用,需要用到功能测试,其中用到了我写的 pytest-docker-pexpect 模块,在一个 docker 容器内设置一个场景来测试所有支持的命令。

发布

The Fuck 应用的最麻烦的部分是它的安装,应用通过 pip 来发布,由此产生了一些问题:

  • 有些平台上依赖 python 的头文件(python-dev),所以我们需要告诉用户手动地安装;
  • pip 不支持安装后自动完成一些自定义操作,所以用户需要手动配置一个别名;
  • 有些用户使用不支持的 python 版本,应用只支持 2.7 或者 3.3+ 的版本;
  • 有些老版本的 pip 根本就不安装依赖项;
  • 有些版本的 pip 忽视 Python 版本的依赖关系,所以需要为早于 3.4 的版本安装 pathlib;
  • 有趣的是有人对这个名字感到很愤怒并且尝试从 pypi 中移除这个包;

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

那个极爽的命令行纠错软件 The Fuck 是如何工作的

推荐阅读
  • 在开发过程中,我最初也依赖于功能全面但操作繁琐的集成开发环境(IDE),如Borland Delphi 和 Microsoft Visual Studio。然而,随着对高效开发的追求,我逐渐转向了更加轻量级和灵活的工具组合。通过 CLIfe,我构建了一个高度定制化的开发环境,不仅提高了代码编写效率,还简化了项目管理流程。这一配置结合了多种强大的命令行工具和插件,使我在日常开发中能够更加得心应手。 ... [详细]
  • jupyterlab部署到docker
    操作环境:macOS10.14.6docker版本:10.03.1终端:iterm23.3时间:2019年8月::说明::jupyter没有提供单独的jupyterlab镜像,可以 ... [详细]
  • 本文探讨了Python类型注解使用率低下的原因,主要归结于历史背景和投资回报率(ROI)的考量。文章不仅分析了类型注解的实际效用,还回顾了Python类型注解的发展历程。 ... [详细]
  • 本文详细记录了使用 Docker 创建和管理自定义网络的命令,包括创建指定子网的网络、删除网络以及在指定网络中运行容器的具体操作。 ... [详细]
  • 1.创建目录mkdir-phomerocketmqnamesvr1data&&mkdir-phomerocketmqnamesvr1log&&mkdir-phomerocketm ... [详细]
  • 本文介绍了如何在 Windows 系统上利用 Docker 构建一个包含 NGINX、PHP、MySQL、Redis 和 Elasticsearch 的集成开发环境。通过详细的步骤说明,帮助开发者快速搭建和配置这一复杂的技术栈,提升开发效率和环境一致性。 ... [详细]
  • Android 图像色彩处理技术详解
    本文详细探讨了 Android 平台上的图像色彩处理技术,重点介绍了如何通过模仿美图秀秀的交互方式,利用 SeekBar 实现对图片颜色的精细调整。文章展示了具体的布局设计和代码实现,帮助开发者更好地理解和应用图像处理技术。 ... [详细]
  • 本文详细介绍了使用响应文件在静默模式下安装和配置Oracle 11g的方法。硬件要求包括:内存至少1GB,具体可通过命令`grep -i memtotal /proc/meminfo`进行检查。此外,还提供了详细的步骤和注意事项,确保安装过程顺利进行。 ... [详细]
  • 深入解析Gradle中的Project核心组件
    在Gradle构建系统中,`Project` 是一个核心组件,扮演着至关重要的角色。通过使用 `./gradlew projects` 命令,可以清晰地列出当前项目结构中包含的所有子项目,这有助于开发者更好地理解和管理复杂的多模块项目。此外,`Project` 对象还提供了丰富的配置选项和生命周期管理功能,使得构建过程更加灵活高效。 ... [详细]
  • 在Python编程中,掌握高级技巧对于提升代码效率和可读性至关重要。本文重点探讨了生成器和迭代器的应用,这两种工具不仅能够优化内存使用,还能简化复杂数据处理流程。生成器通过按需生成数据,避免了大量数据加载对内存的占用,而迭代器则提供了一种优雅的方式来遍历集合对象。此外,文章还深入解析了这些高级特性的实际应用场景,帮助读者更好地理解和运用这些技术。 ... [详细]
  • 在Linux系统中,原本已安装了多个版本的Python 2,并且还安装了Anaconda,其中包含了Python 3。本文详细介绍了如何通过配置环境变量,使系统默认使用指定版本的Python,以便在不同版本之间轻松切换。此外,文章还提供了具体的实践步骤和注意事项,帮助用户高效地管理和使用不同版本的Python环境。 ... [详细]
  • 如何在WAMP环境中更改默认的www根目录路径
    在WAMP环境中更改默认的www根目录路径,可以通过编辑Apache配置文件实现。具体步骤如下:打开D:\WampServer\bin\apache\apache2.2.22\conf\httpd.conf文件,找到并修改DocumentRoot和指令,将路径更改为新的根目录位置。保存文件后重启WAMP服务,即可生效。此方法适用于需要自定义项目部署路径的开发者。 ... [详细]
  • 在腾讯云服务器上部署Nginx的详细指南中,首先需要确保安装必要的依赖包。如果这些依赖包已安装,可直接跳过此步骤。具体命令包括 `yum -y install gcc gcc-c++ wget net-tools pcre-devel zlib-devel`。接下来,本文将详细介绍如何下载、编译和配置Nginx,以确保其在腾讯云服务器上顺利运行。此外,还将提供一些优化建议,帮助用户提升Nginx的性能和安全性。 ... [详细]
  • 在进行网络编程时,准确获取本地主机的IP地址是一项基本但重要的任务。Winsock作为20世纪90年代初由Microsoft与多家公司共同制定的Windows平台网络编程接口,为开发者提供了一套高效且易用的工具。通过Winsock,开发者可以轻松实现网络通信功能,并准确获取本地主机的IP地址,从而确保应用程序在网络环境中的稳定运行。此外,了解Winsock的工作原理及其API函数的使用方法,有助于提高开发效率和代码质量。 ... [详细]
  • 本文深入探讨了Python线程池的内部实现机制,作为对Apshceduler调度器研究的延伸。在先前关于Apshceduler源码分析的文章中,我们提到调度器通过`def_do_submit_`函数将任务提交到线程池。本文将进一步解析线程池的工作原理,包括任务分配、线程管理及性能优化等方面,为读者提供更全面的技术理解。 ... [详细]
author-avatar
阿宅是时候听孙燕姿思_542
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有