Оболочка Имеет Четвертое Качество
Перевод статьи - Shell Has a Forth-like Quality
Автор(ы) - Andy Chu
Источник оригинальной статьи:
Этот пост берет небольшой перерыв от ASDL, чтобы расширить один из моих комментариев к хакерским новостям.
Я понимаю, что не сделал многого из того, о чем упоминал во втором предложении этого блога: объясните, почему оболочка Unix интересна. Отчасти это связано с тем, что почти никто не ставил под сомнение этот проект — похоже, программисты действительно хотят иметь лучшую оболочку.
Но есть некоторые аспекты языкового дизайна, которые встречаются редко, и их стоит объяснить. Это первая из как минимум трех тем.
Настройка демонов Unix
Systemd имеет заявленную цель замены сценариев оболочки в процессе загрузки системы Linux.
В ответ на еще одну тему о компромиссах дизайна systemd agumonkey
он заявил, что shell не обладает достаточной силой абстракции. Вместо этого он предложил шепелявую систему конфигурации:
(->
(path "/usr/bin/some-service" "arg0" ...)
(wrap :pre (lambda () ...)
:post (lambda () ..))
(retry 5)
...
(timeout 5))
Я отметил, что shell уже поддерживает этот вид программирования более высокого порядка. Например, вот функция, которая принимает команду и пробует ее пять раз:
retry() {
local n=$1
shift
for i in $(seq $n); do
"$@"
done
}
Его можно использовать так:
$ retry 5 hello-sleep 0.1
hello
hello
hello
hello
hello
Здесь мы передаем целое число 5 и фрагмент кода hello-sleep 0.1
в функцию retry
. Поскольку retry
попытка обрабатывает код как данные, вы можете назвать его функцией высшего порядка.
Если пойти дальше, то мы можем составить нашу retry
функцию с двоичным таймаутом coreutils
, добавив еще два слова:
$ timeout 0.3 $0 retry 5 hello-sleep 0.1
hello
hello
hello # killed after 0.3 seconds
(Запускаемый код доступен в forth-подобном каталоге репозитория oilshell/blog-code repository).
Поскольку функции могут быть составлены простым сопоставлением, я сказал, что оболочка имеет качество, подобное четвертому .
В языке Forth функции могут быть составлены таким образом, потому что они работают с неявным стеком аргументов и возвращаемых значений. Если это не имеет смысла, этот пост в блоге может помочь.
Оболочка не имеет неявного стека, но равномерное представление слов в argv
массиве и "сращивание" с "$@"
ними приводит к тому, что код кажется похожим.
В отличие от этого, этот механизм не является идиоматическим в Python или JavaScript. Я попробовал портировать demo.sh
на Python с помощью demo.py, и это вроде как работает, если вы пишете все функции, как f(*args)
. Но это идет вразрез с принципами экосистемы. В этих языках функции и аргументы трактуются по-разному с синтаксической и семантической точек зрения.
daemontools и Bernstein Chaining
В книге "Искусство программирования Unix", представляющей собой великолепное изложение философии Unix, Эрик Реймонд называет эту технику цепочкой Бернштейна.
Бернштейн использует эту технику оболочки в таких программах, как qmail и daemontools, чтобы следовать принципу наименьших привилегий.
В отличие от systemd, daemontools init
- это инструментарий Unix, который опирается на идиому небольших C-программ, составленных с помощью shell-скриптов. Празднование daemontools хорошо обосновывает это и показывает примеры. Вот выдержка , в которой используется бернштейновская цепочка setuidgid
иsoftlimit
, а также встроеннаяexec
:
# change to the user 'sample', and then limit the stack segment
# to 2048 bytes, the number of open file descriptors to 3, and
# the number of processes to 1:
exec setuidgid sample \
softlimit -n 2048 -o 3 -p 1 \
some-small-daemon -n
Daemontools минимально документирован и не видит большого применения сегодня, но runit имеет ту же архитектуру, а также набор крошечных скриптов оболочки, которые иллюстрируют его использование.
По общему признанию, они немного загадочны, но архитектура-это то, что меня волнует. systemd действительно отделяет некоторые из этих функций в отдельном двоичном файле systemd-nspawn, но он, похоже, не очень часто используется без остальной части systemd.
Вывод
daemontools и systemd интересны тем, что представляют собой крайности по отношению к модульности их конструкции.
Поскольку я пишу оболочку, неудивительно, что я предвзято отношусь к стилю daemontools. Но systemd справедливо критикует шелл-скрипты. Язык имеет много проблем, одним из примеров которых является синтаксис массива.
С другой стороны, я не удивлюсь, если конфигурация systemd случайно станет полной по Тьюрингу, как это сделали shell и make.
Я не знаю, каков лучший ответ, но я думаю, что улучшенная оболочка поможет ситуации. По крайней мере, в шепелявости нет необходимости. С oil
, я стремлюсь сохранить вечные архитектурные характеристики shell, отказываясь от уродливого, непоследовательного синтаксиса и сглаживая его острые углы.
Приложение: Команды, которые составляют
Вот список инструментов, которые могут быть составлены таким образом:
sudo
: Выполните команду от имени другого пользователя.chroot
: Выполните команду с другим корневым каталогом.env
: Выполните команду в другой среде./usr/bin/time
: Выполните команду, и система подведет итог использования ресурсов.su
: изменить идентификатор пользователя. Это имеет сомнительный интерфейс взятия строки оболочки-c
вместо передачи ее оставшихся аргументов, что приводит к проблемам с цитированием.ssh
: Выполните команду. Также есть проблемы с цитированием.strace
: Отслеживание системных вызовов и сигналов.- systemd-nspawn
- Большинство инструментов в daemontools, runit и других наборах инструментов , таких как s6
gdb
: Отладка собственных программ.
Это встроенные оболочки, которые составляют:
exec
: Замените образ процесса; оболочка дляexec()
системного вызова.time
: В bash это встроенный модуль , который также принимает блок, напримерtime { echo 1; echo 2; }
.command
иbuiltin
еще: Измените порядок поиска первого слова — это внешняя команда в$PATH
оболочке или внутренняя?
Обновления (2017/1/24)
-
Спасибо Эрику Визеру за исправление стиля версии Python. Паттерн работает лучше , если каждая функция выглядит так
myfunc(myarg, f, *args)
, но я бы все же сказал, что он идет вразрез с зерном экосистемы, как уже упоминалось выше. -
В следующем посте этой серии также упоминается Forth, потому что функции в Forth составляются в стиле без точек. Цепочка Бернштейна не совсем свободна от точек , потому что мы уже упоминали
"$@"
, но конвейеры действительно составляются в стиле без точек.