кажется, что Bash - тьюрингово-полный язык
Понятие полноты по Тьюрингу полностью отделено от многих других понятий, полезных в языке для программирования в целом : удобство использования, выразительность, понимание, скорость и т. Д.
Если Тьюринг-полнота были все мы требовали, мы бы не имели каких - либо языков программирования на всех , даже не ассемблере . Все программисты просто пишут в машинном коде , так как наши процессоры также полны по Тьюрингу.
почему Bash используется почти исключительно для написания относительно простых скриптов?
Большие, сложные сценарии оболочки - такие как configure
сценарии, выводимые GNU Autoconf, - нетипичны по многим причинам:
До относительно недавнего времени вы не могли рассчитывать на наличие POSIX-совместимой оболочки везде .
Многие системы, особенно старые, технически имеют POSIX-совместимую оболочку где-то в системе, но она может не находиться в таком предсказуемом месте, как /bin/sh
. Если вы пишете сценарий оболочки, и он должен работать на разных системах, как тогда вы пишете строку shebang ? Один из вариантов - пойти дальше и использовать /bin/sh
, но выбрать ограничиться диалектом пред-POSIX Bourne, если он запускается в такой системе.
Оболочки Борна до POSIX даже не имеют встроенной арифметики; Вы должны позвать expr
или bc
сделать это.
Даже с оболочкой POSIX вы упускаете ассоциативные массивы и другие функции, которые мы ожидали найти в языках сценариев Unix с тех пор, как Perl впервые стал популярен в начале 1990-х годов .
Этот исторический факт означает, что существует давняя традиция игнорировать многие из мощных функций современных интерпретаторов сценариев оболочки семейства Борнов просто потому, что вы не можете рассчитывать на их повсеместное использование.
Фактически это продолжается и по сей день: Bash не получал ассоциативные массивы до версии 4 , но вы можете быть удивлены тем, сколько систем, которые все еще используются, основаны на Bash 3. Apple все еще поставляет Bash 3 с macOS в 2017 году - очевидно, для причины лицензирования - и серверы Unix / Linux часто работают практически без изменений в течение очень долгого времени, так что у вас может быть стабильная старая система с Bash 3, например, с CentOS 5. Если у вас есть такие системы в вашей среде, вы не можете использовать ассоциативные массивы в сценариях оболочки, которые должны работать на них.
Если ваш ответ на эту проблему в том , что вы только писать скрипты для «современных» систем, то вы должны справиться с тем , что последней общей опорной точкой для большинства Unix оболочка является стандартом POSIX оболочки , которая в значительной степени неизменный , поскольку он был введено в 1989 году. Существует много различных оболочек, основанных на этом стандарте, но все они в той или иной степени отличаются от этого стандарта. Для того, чтобы ассоциативные массивы снова bash
, zsh
и ksh93
все они имеют эту функцию, но есть несколько несовместимостей реализации. Таким образом, ваш выбор - использовать только Bash, или только Zsh, или только использовать ksh93
.
Если ваш ответ на эту проблему таков: «просто установите Bash 4» ksh93
или что-то еще, то почему бы не «просто» установить вместо этого Perl, Python или Ruby? Это неприемлемо во многих случаях; значения по умолчанию.
Ни один из языков сценариев оболочки семейства Bourne не поддерживает модули .
Наиболее близкой к модульной системе в сценарии оболочки является .
команда - также известная source
в более современных вариантах оболочки Bourne - которая дает сбой на нескольких уровнях по отношению к надлежащей модульной системе, самым основным из которых является пространство имен .
Независимо от языка программирования, человеческое понимание начинает отмечаться, когда любой отдельный файл в более крупной общей программе превышает несколько тысяч строк. Сама причина, по которой мы структурируем большие программы во множество файлов, заключается в том, что мы можем абстрагировать их содержание не более чем в одно или два предложения. Файл A - это синтаксический анализатор командной строки, файл B - сетевой насос ввода-вывода, файл C - это прокладка между библиотекой Z и остальной частью программы и т. Д. Когда единственным способом для объединения множества файлов в одну программу является включение текста Вы устанавливаете ограничение на разумный рост ваших программ.
Для сравнения это было бы как если бы язык программирования C не имел компоновщика, только #include
операторы. Такой диалект C-Lite не нуждается в ключевых словах, таких как extern
или static
. Эти функции существуют для обеспечения модульности.
POSIX не определяет способ для выделения переменных в одной функции сценария оболочки, а тем более в файле.
Это эффективно делает все переменные глобальными , что опять-таки вредит модульности и компоновке.
Есть решения для этого в оболочках после POSIX - конечно bash
, ksh93
и zsh
по крайней мере - но это только возвращает вас к пункту 1 выше.
Вы можете увидеть эффект этого в руководствах по стилю при написании макросов GNU Autoconf, где они рекомендуют ставить имена переменных перед именем самого макроса, что приводит к очень длинным именам переменных исключительно для того, чтобы уменьшить вероятность столкновения до приемлемо близкого нуль.
Даже С лучше на этот счет на милю. Мало того, что большинство программ на C написаны в основном с локальными переменными функций, C также поддерживает область видимости блоков, позволяя нескольким блокам в одной функции повторно использовать имена переменных без перекрестного загрязнения.
Языки программирования оболочки не имеют стандартной библиотеки.
Можно утверждать, что стандартная библиотека языка сценариев оболочки является содержимым PATH
, но это просто говорит о том, что для достижения каких-либо последствий, сценарий оболочки должен вызывать другую целую программу, вероятно, написанную на более мощном языке, чтобы начинать с.
Также нет широко используемого архива служебных библиотек оболочки, как в CPAN Perl . Без большой доступной библиотеки стороннего служебного кода программист должен писать больше кода вручную, поэтому он менее продуктивен.
Даже игнорируя тот факт, что большинство сценариев оболочки полагаются на внешние программы, обычно написанные на C, для выполнения чего-либо полезного, есть издержки всех этих pipe()
→ fork()
→ exec()
цепочек вызовов. Этот шаблон довольно эффективен в Unix по сравнению с IPC и запуском процессов в других ОС, но здесь он фактически заменяет то, что вы делаете, вызовом подпрограммы на другом языке сценариев, который все еще гораздо эффективнее. Это серьезно ограничивает скорость выполнения сценариев оболочки.
Сценарии оболочки имеют мало встроенных возможностей для повышения производительности за счет параллельного выполнения.
Bourne снарядов &
, wait
и трубопроводы для этого, но это в основном используется только для составления нескольких программ, а не для достижения CPU или параллелизм ввода / вывода. Вы вряд ли сможете привязать ядра или насытить массив RAID исключительно с помощью сценариев оболочки, и если вы это сделаете, вы, вероятно, сможете добиться гораздо более высокой производительности на других языках.
Конвейеры, в частности, являются слабым способом повышения производительности за счет параллельного выполнения. Он позволяет только двум программам работать параллельно, и одна из двух, вероятно, будет заблокирована при вводе-выводе в другую или в любой другой момент времени.
Есть новоявленные пути вокруг этого, такие , как xargs -P
и GNUparallel
, но это просто переходит к пункту 4 выше.
Поскольку встроенные возможности не позволяют полностью использовать преимущества многопроцессорных систем, сценарии оболочки всегда будут работать медленнее, чем хорошо написанная программа на языке, который может использовать все процессоры в системе. Чтобы configure
снова взять этот пример сценария GNU Autoconf , удвоение количества ядер в системе мало что изменит для повышения скорости ее работы.
Языки сценариев оболочки не имеют указателей или ссылок .
Это мешает вам делать кучу вещей, которые легко сделать на других языках программирования.
Во-первых, невозможность косвенной ссылки на другую структуру данных в памяти программы означает, что вы ограничены встроенными структурами данных . Ваша оболочка может иметь ассоциативные массивы , но как они реализованы? Есть несколько возможностей, каждая из которых имеет свой компромисс: красно-черные деревья , деревья AVL и хеш-таблицы являются наиболее распространенными, но есть и другие. Если вам нужен другой набор компромиссов, вы застряли, потому что без ссылок у вас не будет возможности вручную прокрутить многие типы сложных структур данных. Вы застряли с тем, что вам дали.
Или, может быть, вам нужна структура данных, в которой даже нет адекватной альтернативы, встроенной в интерпретатор сценария оболочки, такой как направленный ациклический граф , который может вам понадобиться для моделирования графа зависимостей . Я программировал десятилетиями, и единственный способ, которым я мог придумать, чтобы сделать это в сценарии оболочки, - это злоупотреблять файловой системой , используя символические ссылки в качестве ложных ссылок. Это решение, которое вы получаете, когда полагаетесь только на полноту Тьюринга, которая ничего не говорит о том, является ли решение элегантным, быстрым или простым для понимания.
Усовершенствованные структуры данных - это всего лишь одно использование указателей и ссылок. Для них есть куча других приложений , которые просто невозможно сделать с помощью языка сценариев оболочки семейства Борнов.
Я мог бы продолжать и продолжать, но я думаю, что вы здесь понимаете. Проще говоря, есть много более мощных языков программирования для систем типа Unix.
Это огромное преимущество, которое в некоторых случаях может компенсировать посредственность самого языка.
Конечно, именно поэтому GNU Autoconf использует целенаправленно ограниченное подмножество языков сценариев оболочки Bourne для своих configure
выходных данных сценариев: так что его configure
сценарии будут работать практически везде.
Вы, вероятно, не найдете большую группу сторонников полезности написания на очень переносимом диалекте оболочки Bourne, чем разработчики GNU Autoconf, хотя их собственное творение написано в основном на Perl, плюс некоторые m4
, и только немного оболочки сценарий; только вывод Autoconf - это скрипт оболочки Bourne. Если это не вызывает вопроса о том, насколько полезна концепция «Борн везде», я не знаю, что будет.
Итак, есть ли предел сложности таких программ?
Технически говоря, нет, как предполагает ваше наблюдение за полнотой по Тьюрингу.
Но это не то же самое, что сказать, что произвольно большие скрипты оболочки приятно писать, легко отлаживать или быстро выполнять.
Можно ли написать, скажем, файловый компрессор / декомпрессор в чистом bash?
«Чистый» Баш, без каких-либо призывов к вещам в PATH
? Компрессор, вероятно, выполним с использованием echo
шестнадцатеричных escape-последовательностей, но это было бы довольно болезненно. Распаковщик может быть невозможно записать таким образом из-за невозможности обработки двоичных данных в оболочке . В конечном итоге вы будете вызывать od
и тому подобное для перевода двоичных данных в текстовый формат, родной способ обработки данных оболочкой.
Как только вы начинаете говорить об использовании сценариев оболочки так, как это было задумано, в качестве клея для управления другими программами PATH
, двери открываются, потому что теперь вы ограничены только тем, что можно сделать на других языках программирования, то есть вы не имеет ограничений вообще. Скрипт оболочки , который получает весь его силу, вызвав к другим программам в PATH
не работает так быстро , как монолитные программы , написанных на более мощных языках, но это действительно работать.
И в этом все дело. Если вам нужна программа для быстрого запуска или если она должна быть мощной сама по себе, а не заимствовать силы у других, вы не пишете ее в оболочке.
Простая видеоигра?
Вот тетрис в оболочке . Другие такие игры доступны, если вы идете смотреть.
Есть только очень ограниченные средства отладки
Я бы поставил поддержку средств отладки примерно на 20-е место в списке функций, необходимых для поддержки программирования в целом. Многие программисты в большей степени полагаются на printf()
отладку, чем на правильные отладчики, независимо от языка.
В оболочке у вас есть echo
и set -x
, что в совокупности достаточно для отладки множества проблем.
sh
Скрипт ,configure
который используется как часть процесса сборки для очень многого ипа * х пакетов не является «относительно простым».