Оболочка ksh в OpenBSD и как ее вкусно приготовить.
Опубликовано 19.06.2024 в OpenBSD
Давным-давно, в одной далекой галактике...
На заре unix-time, когда, как известно, деревья были большими, трава зеленее, водка крепче только-только зародился этот ваш UNIX, командной оболочкой его стала программа sh, написанная в 1978 году Стефаном Борном. Язык её командного интерпретатора был основан на передовом для тех времен Algol 68, а вот в части интерактивности там был полный швах: никаких столь привычных нам автодополнений путей и команд, средств интерактивного редактирования, истории команд и информативных приглашений там не было и в помине.
Чуть позже расцвела целая клумба шеллов (я не буду сейчас углубляться в подробности, об этом написаны книги, есть сравнительные таблицы...), ограничусь лишь кратким перечислением: в 1980-х в AT&T появился шелл Дэвида Корна ksh, чуть более продвинутый, в университете Беркли зародилась несовместимая по синтаксису скриптов (тяготеющая к языку C) оболочка csh - там-то вроде впервые и возникло большинство современных интерактивных плюшек, ребята из проекта FreeBSD вспомнили про оболочку Кэна Грига из университета Каргени-Меллон tcsh (она создавалась по образу командного интерпретатора ОС TENEX, а отличительной особенностью этой ОС были длиннющие зубодробильные команды, отсюда в tcsh родились наиболее развитые алгоритмы автодополнения и поиска по истории, позднее вошедшие в том или ином виде во все современные шеллы), в мире GNU/Linux начал свое победное шествие bash, в MasOS с некоторых пор стандартной оболочкой стала еще более фичастая zsh...
Но упомянутая мною оболочка Корна ksh заняла место стандартной в AIX и QNX. При этом, код оригинальной ksh до самых 2000-х оставался закрытым, что породило ряд форков - появилась, в частности, pdksh (public domain Korn shell) и вот ее уже много-много лет назад форкнул проект OpenBSD, где ныне ksh и является дефолтной оболочкой в базовой системе. Да, разумеется, в OpenBSD доступны к установке любые другие шеллы из современного (и не очень) зоопарка: bash, zsh, fish, tcsh... Но философия проекта OpenBSD предполагает выбор по умолчанию пусть не самых фичастых, зато простых в устройстве, сопровождении и развитии решений. Поэтому ksh.
Базовые настройки
Скрипт ~/.kshrc
является одним из умлочальных расположений настроек (другие описаны в man ksh
) и у меня в оный добавлены следующие плюшки:
Экспорт некоторых переменных окружения:
export EDITOR=nvim
export VISUAL=nvim
export FCEDIT=$EDITOR
export PAGER=less
export LESS='-iMRS -x2'
export LC_CTYPE=en_US.UTF-8
export CLICOLOR=1
export XAUTHORITY=~/.Xauthority
export GOPATH=~/go
Настройки расположения и размера файла с историей команд:
HISTFILE=$HOME/.ksh_history
HISTSIZE=20000
Стиль редактирования командной строки (поддерживаются emacs-сочетания и режим vi), я хоть и старый вимер, но все-таки хоткеи оболочки въелись в мою плоть и кровь еще раньше освоения vi/vim/nvim, так что:
set -o emacs
Немножко alias для упрощения жизни (думаю, пояснения тут излишни):
alias ls="$LS -FHh"
alias ll='ls -l'
alias la='ls -lA'
alias ..='cd ..'
alias ...='cd ...'
alias mkdir='mkdir -p'
alias df='df -h'
alias du='du -ch'
alias svim="doas vim"
alias svi="doas vi"
alias cvsup='cvs -q up -Pd -A'
alias nv='nvim'
alias snv='doas nvim'
Настройки приглашения командой строки, не перегруженного, но симпатичного (читайте маны, мне эта тарабарщина тоже с трудом дается):
_PID=$$
_XTERM_TITLE='\[\033]0;\u@\w:\w\007\]'
_PS1_CLEAR='\[\033[0m\]'
_PS1_BLUE='\[\033[34m\]'
case "$(id -u)" in
0) _PS1_COLOR='\[\033[1;31m\]' ;;
*) _PS1_COLOR='\[\033[32m\]' ;;
esac
PS1='$_XTERM_TITLE\t $_PS1_COLOR\u$_PS1_CLEAR $_PS1_BLUE\w $_PS1_COLOR\$$_PS1_CLEAR '
Чуть-чуть удобных биндингов: например, вы начали печатать команду, с запозданием вспомнив, что ее нужно запускать от рута? В любой момент нажимаем ESC, затем d - и в начале строки чудесным образом подставляется doas. Второй биндинг для отбитых на голову вимеров, кто периодически пытается выйти из шелла через ":q" :))
bind -m '^[d'='^Adoas ^E'
bind -m ':q'='exit^M'
Да вот, пожалуй, и всё. Добавлю лишь, что экспорт ряда переменных окружения, вроде PATH или ENV я держу, по многолетней привычке, в ~/.profile
, а некоторые вещи, специфичные для GUI - в ~/.xsession
.
Автодополнение для команд после doas и man-страниц
Теперь к интересному. ksh не располагает такими развесистыми изкоробочными средствами автодополнения, как tcsh, zsh или bash + bash_completion, например, умеет из коробки в автодополнение команд, но если тебе нужно запустить что-то от рута (то есть, ввести сначала doas, а затем команду) - тут облом-с. А еще не умеет в автодополнение по man-страницам. Но решение есть - ksh умеет в автодополнение из файла, так что нам нужно лишь создать файл кеша, содержащий все имена программ и man-страниц для поиска (для удобства я держу нижеприведенный код в отдельном файле ~/scripts/ksh_functions.ksh
:
PROG_CACHE=$HOME/.cache/prog
if [ ! -f $PROG_CACHE ]; then
mkdir -p $(dirname ${PROG_CACHE})
for directory in $(echo $PATH | tr ':' '\n')
do
find $directory -type f -perm -111 -exec basename '{}' ';'
done | sort -u > $PROG_CACHE
fi
MAN_CACHE=$HOME/.cache/man
if [ ! -f $MAN_CACHE ]; then
mkdir -p $(dirname ${MAN_CACHE})
for i in /usr{,/X11R6,/local}{,/share}/man/{,cat,man}[1-9lnp]{,f,p}
do
test -d $i && ls $i
done | rev | cut -d. -f2- | rev | sort -u > $MAN_CACHE
fi
Чтобы наполнять этот кеш, создадим в ~/.kshrc
alias (а чтобы не делать этого каждый раз руками после установки/удаления софта, добавим его в crontab):
alias recache='rm -f $HOME/.cache/man && rm -f $HOME/.cache/prog && /bin/ksh -e $HOME/scripts/ksh_functions.ksh'
. $HOME/scripts/ksh_functions.ksh
Теперь при каждом запуске ksh или вручную, при вводе команды recache, будут создаваться кеш-файлики со списком всех программ и man-страниц, ну а чтобы по ним работало автодополнение, добавим в ~/.kshrc
вот это:
set -A complete_doas_1 -- $(cat $PROG_CACHE)
set -A complete_man -- $(cat $MAN_CACHE)
И еще чуть-чуть удобных автодополнений
А вот комфортная автодополнялка для средства запуска служб в OpenBSD: вводишь rcctl, затем уже с возможностью автодополнения управляющую команду, а после, третьим словом (тоже уже с автодополнением) имя службы:
set -A complete_rcctl_1 -- disable enable get ls order set restart start stop
set -A complete_rcctl_2 -- $(rcctl ls all)
Есть подобное и для работы с гитом:
set -A complete_git_1 -- \
$(git --list-cmds=main) \
$(git config --get-regexp ^alias\. | awk -F '[\.]' '{ print $2 }')
Быстрая навигация по файловой системе
Еще подсмотренная мною у кого-то полезняшка, быстрая навигация по наиболее употребимым местам в файловой системе. Пишем несложную функцию:
k() {
if [ -z $1 ]; then
echo $PWD >> ~/.k
else
K=~/.k
case $1 in
clean) sort -u $K -o ${K};;
rm) sed -i -E "\#^${2:-${PWD}}\$#d" ${K};;
ls) cat ${K};;
*) cd "$(grep -e "$1" ${K} | head -n 1)";;
esac
fi
}
Работает так: перейдя в какую-то часто посещаемую директорию, например, /usr/ports, пишем в командной строке k
для занесения ее в список часто посещаемых. В дальнейшем, ввод краткого k por
или даже k po
переносит нас в /usr/ports, что весьма удобно. k ls
покажет список всех сохраненных популярных мест, k rm
удалит текущее расположение из списка, k clean
отсортирует и дедублицирует список для пущей скорости поиска.
Игого
Резюмирую: ksh с вышеперечисленными плюшками более чем полностью покрывает все мои потребности в интерактивности и удобстве на текущий момент. Переезжать на zsh, tcsh, fish, bash и тому подобное - не вижу никакой необходимости, мой дефолтный ksh резв и удобен до безобразия.