2 November 2014

ah — лучше, чем history

*nix
Так получается, что я провожу в консоли (терминале) достаточно времени, порой даже больше, чем хотелось бы. Порой даже исполняю там какие-то команды и внимательно изучаю их вывод. Часто случается, что к выводу той или иной команды приходится возвращаться, а он постоянно теряется: то терминалы захлопываются, то в tmux окно закрываешь, то выводы прочих команд уже давным-давно забили и похоронили ту самую полезную строчку.

Для того, чтобы сохранить вывод какой-либо утилиты я, как и многие, пользовался tee. Это работало, но постоянная суета среди бесконечных error.log, out.log, output.log, err.log log.log, lll.txt и тп если не сводила с ума, то безумно раздражала; вместо того, чтобы вести какой-то порядок, постоянно подмывало создать Новую Папку (1), где и похоронить эти самые логи, периодически бэкапя могильничек: порядок предполагал какую-то систематизацию, а в разгаре работы вспоминать как назвать свой файл крайне не хотелось.

Тогда я написал ah, крохотную утилитку, которая сильно-сильно улучшила мою жизнь.

ah предполагалась небольшим дополнением к встроенной команде history, которая есть практически в каждом шеле; однако ah умеет работать лишь с двумя: zsh (поскольку я им сам пользуюсь) и bash (поскольку им пользуются практически все, кто не пользуется zsh). Это ни в коем случае не замена history, скорее это дополнение к ней. ah, в основном, умеет 4 вещи: показывать history, сохранять вывод потоков команд, привязывать его к номеру в истории, и показывать по запросу. Кроме того, есть еще такая мелочь, как закладки (любой записи из истории можно дать имя).

Сохранение вывода команды


ah умеет сохранять объединенный вывод команды, причем каталогизацией занимается сама. В самом простейшем случае достаточно сделать вот так:

➜ ah t -- find ./app -name "*.go" -type f
./app/historyentries/get_commands.go
./app/historyentries/parser.go
./app/historyentries/keeper.go
./app/historyentries/history_entry.go
./app/historyentries/history_processor.go
./app/environments/environments.go
./app/utils/re.go
./app/utils/logging.go
./app/utils/synchronized_writer.go
./app/utils/exec.go
./app/utils/utils.go
./app/commands/bookmark.go
./app/commands/remove_bookmarks.go
./app/commands/gc.go
./app/commands/list_trace.go
./app/commands/tee.go
./app/commands/execute.go
./app/commands/show.go
./app/commands/list_bookmarks.go
./app/slices/slices.go


Вывод сохраняется (причем объединенный, как с stdout, так и с stderr), и его можно запросить в дальнейшем. В чем же отличие от, скажем, tee? С tee можно тоже написать подобное

➜ find ./app -name "*.go" -type f |& tee output.log


На самом деле, эти команды не равнозначные. Дело в том, что для tee мы перенаправляем stderr в stdout, тем самым теряя возможность их фильтрации после tee. ah же сохраняет это разделение. Иными словами, мы можем написать

➜ ah t -- find ./app -name "*.go" -type f > /dev/null


И получить на экране только вывод stderr. А что сохранит ah? Он сохранит оба потока. И код возврата. Да, ah завершается с тем же кодом, с которым отработала предыдущая команда. Еще ah нормально работает с ssh, причем даже если запускать там ncurses-приложения. Если нужно, есть поддержка псевдо-TTY и возможность запуска в реальном интерактивном шеле.

Показ истории



➜ ah s 10
...
!10109  (02.11.14 18:05:14)    nvim main.go
!10110  (02.11.14 21:48:12) *  ah t -- find ./app -name "*.go" -type f


Да, ah умеет показывать содержимое вашего HISTFILE и знает про HISTTIMEFORMAT. Угадайте, зачем рядом с номером стоят восклицательные знаки. А вот что значит звезда перед ah t...? Это значит, что ah хранит вывод этой команды. Посмотреть вывод можно с помощью субкоманды l.

➜ ah l 10110
./app/historyentries/get_commands.go
./app/historyentries/parser.go
./app/historyentries/keeper.go
./app/historyentries/history_entry.go
./app/historyentries/history_processor.go
./app/environments/environments.go
./app/utils/re.go
./app/utils/logging.go
./app/utils/synchronized_writer.go
./app/utils/exec.go
./app/utils/utils.go
./app/commands/bookmark.go
./app/commands/remove_bookmarks.go
./app/commands/gc.go
./app/commands/list_trace.go
./app/commands/tee.go
./app/commands/execute.go
./app/commands/show.go
./app/commands/list_bookmarks.go
./app/slices/slices.go


Кстати, 10 в ah s 10 означает буквально «показать последние 10 команд». В то же время поддерживается синтаксис слайсов (1:1 как в Python): ah s 10 20 покажет все команды с 11 по 20, ah s 10 _20 — с 11 по 20 с конца (_, но не -). Еще можно искать по регулярным выражениям + есть примитивный fuzzy matching.

Кроме того, можно делать закладки с субкомандой b, листать их с lb, удалять с rb, чистить старые выводы с gt, но это уже мелочи.

Надеюсь, что кому-нибудь еще жить станет легче.

So there.
github.com/9seconds/ah
Tags:shellhistory
Hubs: *nix
+89
30k 203
Comments 21
Popular right now
Системный администратор Linux
from 100,000 ₽Kintech LabМосква
системный администратор, администратор сети ПД
from 60,000 to 120,000 ₽Первый ТелекомМосква
Старший системный администратор
from 100,000 to 100,000 ₽ООО "Унилинк"Москва
Cloud IT Systems Administrator
from 270,000 to 320,000 ₽ОТП БанкМосква
Developer C#
from 100,000 to 180,000 ₽АМ-интеллектуальные технологииRemote job