Всем привет! Часто ли вы опечатываетесь во время работы в терминале? Уверен, что каждый хоть раз забывал sudo
или вместо cd
писал cs
. Как-то раз увидел алиас fuck
для добавления sudo
к предыдущей команде. Я заинтересовался и начал копать.
Первое решение заслуживающее уважения
В своих поисках нашел утилиту thefuck. Кроме простого добавления sudo
там где это нужно, она может исправить опечатку в команде, в под команде, добавить недостающие флаги и много другого.
Но для себя я нашел 2 недостатка: программа написана на Python из-за чего она может быть немного медленной и программа давно не поддерживается, соответственно не работает на новых версиях языка.
Быстрая альтернатива
Мы же тут программисты, подумал я, и решил написать альтернативу на Rust под названием theshit. Честно говоря, это был мой первый опыт с Rust от этого было еще интереснее. Автор thefuck
применил очень интересный подход по передаче последней команды в программу, который я любязно позаимствовал. Мы не вызываем нашу программу напрямую, а создаем специальный алиас(функцию) для shell'а.
Пример алиаса theshit для zsh:
{name}() { # {name} - имя алиаса
export SH_SHELL=zsh;
SH_PREV_CMD=\"$(fc -ln -1)\";
export SH_PREV_CMD;
SH_SHELL_ALIASES=$(alias);
export SH_SHELL_ALIASES;
SH_CMD=$(
{} fix $@ # {} - путь к программе
) && eval \"$SH_CMD\";
unset SH_PREV_CMD;
unset SH_SHELL;
unset SH_SHELL_ALIASES;
}
Как видите мы всю нужную информацию передаем через переменные окружения и встроенные в оболочку команды. А как мы исправляем команды? Собственно также как и в thefuck
, у нас есть некие "правила", которые состоят из 2 функций - проверки и исправления.
Пример правила для исправления под команды cargo
:
pub fn is_match(command: &Command) -> bool {
command.output().stderr().contains("no such command")
&& command
.output()
.stderr()
.contains("a command with a similar name exists:")
&& command.parts()[0] == "cargo"
}
pub fn fix(command: &Command) -> String {
let broken = &command.parts()[1];
let fix = Regex::new(r"a command with a similar name exists: `([^`]*)`")
.unwrap()
.captures(command.output().stderr())
.and_then(|caps| caps.get(1))
.map(|m| m.as_str())
.unwrap();
misc::replace_argument(command.command(), broken, fix)
}
Когда мы пишем команду с ошибкой, cargo
сам говорит правильный аналог. Например:
❯ cargo builrt
error: no such command: `builrt`
help: a command with a similar name exists: `build`
help: view all installed commands with `cargo --list`
help: find a package to install `builrt` with `cargo search cargo-builrt`
Мы в свою очередь просто проверяем есть ли подсказка и вытаскиваем ее.
Заключение
Я вам рассказал о 2 утилитах для коррекции ошибок и принцип их работы. Надеюсь вам понравилось и вы будете быстрее работать в терминале. А на последок хочу попросить помощи в разработке и тестировании theshit - открывайте issue, присылайте пулл реквесты, а главное - не бойтесь экспериментировать.