Наскільки складно може бути написана програма на чистому Bash? [зачинено]


17

Після кількох дуже швидких досліджень, здається, що Баш є мовою, якою є повний Тьюрінг .

Цікаво, чому Баш використовується майже виключно для написання відносно простих сценаріїв? Оскільки оболонка Bash поставляється з Linux, ви можете запускати скрипти оболонки без зовнішнього перекладача чи компілятора, як це потрібно для інших популярних мов комп'ютера. Це величезна перевага, яка могла б компенсувати посередність самої мови в деяких випадках.

Отже, чи існує обмеження щодо складності таких програм? Чистий Bash використовується для написання складних програм? Чи можливо написати, скажімо, компресор / декомпресор файлу в чистому Bash? Компілятор? Проста відеоігра?

Це настільки рідко використовується лише тому, що є лише дуже обмежені інструменти налагодження?


2
shСкрипт , configureякий використовується як частина процесу складання для дуже багато чого іпа * х пакетів не є «відносно простим».
користувач4556274

@ user4556274 Це не так, але зазвичай пишеться не від руки, а з великого набору m4макросів.
Kusalananda

2
У Bash є асемблер x86 , тому так, Bash періодично використовується для написання складних програм. Чому люди не роблять цього частіше? Можливо тому, що перекладач також повільний, шалений і він схильний до "цікавих" помилок (див. Fi Shellshock ). Крім того, сценарії Bash, як правило, отримують експоненціально важче підтримання розміру. Подивіться на асемблер вище; Ви можете сказати з джерела, якщо він відповідає синтаксису AT&T чи Intel?
Satō Katsura

configureСценарії також повільні, роблять цілу купу марної роботи і були предметом забавних зусиль. Звичайно, оболонку можна використовувати для великих програм, але потім люди також зробили комп'ютери з гри «Життя життя та Minecraft» Conway , а також є мови програмування, такі як Brainf ** k та Hexagony . Мабуть, деякі просто люблять будувати щось із справді маленьких та заплутаних атомів. Ви навіть можете продати ігри з цією ідеєю ...
ilkkachu

Отже, це питання відповідає чи ні? Вони призупиняють і кажуть, що це невиправдано, але все ж я отримую кілька чудових відповідей. Було б непогано бути когерентним, оскільки я новачок у цій ДП, щоб спрямувати мене на те, які питання є та чи не бажані на цьому ПП.
Брегалад

Відповіді:


30

здається, що Баш - мова, якою є Тюрінг

Концепція повноти Тьюрінга повністю відокремлена від багатьох інших понять, корисних у мові для програмування в цілому : зручність, виразність, зрозумілість, швидкість тощо.

Якщо Тьюринг-повнота були всі ми вимагали, ми б не мали жодних - або мов програмування на всіх , навіть не асемблері . Комп'ютерні програмісти просто записують у машинному коді , оскільки наші процесори також завершені Тьюрінгом.

чому Bash використовується майже виключно для написання відносно простих сценаріїв?

Великі складні сценарії оболонки - такі як configureсценарії, виведені GNU Autoconf - з багатьох причин нетипові:

  1. Ще зовсім недавно ви не могли розраховувати на те, щоб мати повну сумісність з POSIX .

    Багато систем, особливо старіші, технічно мають оболонку, сумісну з POSIX, десь у системі, але вона може не знаходитися в передбачуваному місці /bin/sh. Якщо ви пишете сценарій оболонки, і він повинен працювати в багатьох системах, як тоді ви пишете рядок shebang ? Один із варіантів - продовжувати та користуватися /bin/sh, але вибирайте обмежити себе діалектом оболонки до POSIX Bourne, якщо він запускається на такій системі.

    Оболонки пре-POSIX Bourne навіть не мають вбудованої арифметики; вам доведеться зателефонувати 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 замість цього Це у багатьох випадках неприпустимо; значення за замовчуванням мають значення.

  2. Жоден з модулів підтримки мов скриптів сімейства Bourne .

    Найближчим до модульної системи в сценарії оболонки є .команда, яка називається sourceв більш сучасних варіантах оболонки Bourne, яка не працює на декількох рівнях відносно належної модульної системи, основним з яких є пробіг імен .

    Незалежно від мови програмування, людське розуміння починає позначатися, коли будь-який файл у більшій загальній програмі перевищує кілька тисяч рядків. Сама причина, по якій ми структуруємо великі програми в багато файлів, полягає в тому, що ми можемо абстрагувати їхній вміст максимум до речення або двох. Файл A - це аналізатор командного рядка, файл B - це мережевий насос вводу / виводу, файл C - проміжок між бібліотекою Z та рештою програми тощо. Коли ваш єдиний метод для збирання багатьох файлів в одну програму - це текстове включення , ви ставите обмеження на те, наскільки ваші програми можуть рости розумно.

    Для порівняння, це було б так, якби мова програмування C не мала лінкера, а лише #includeзаяви. Така С-полегшений говір не потрібні ключові слова , такі як externабо static. Ці функції існують, щоб дозволити модульність.

  3. POSIX не визначає спосіб застосування змінних до однієї функції скрипту оболонки, тим більше до файлу.

    Це ефективно робить усі змінні глобальними , що знову ж таки шкодить модульності та компонованості.

    Для цього є рішення в оболонках після POSIX - звичайно bash, в принаймні , ksh93і zshхоча б - але це просто повертає вас до точки 1 вище.

    Ефект цього можна побачити в посібниках зі стилів для написання макросів GNU Autoconf, де вони рекомендують встановити прізвища змінних з іменем самого макросу, що веде до дуже довгих імен змінних виключно для того, щоб знизити ймовірність зіткнення до близько нуль.

    Навіть С кращий за цією оцінкою, на милю. Не тільки більшість програм C, написаних головним чином за допомогою локальних змінних функцій, C також підтримує масштабування блоків, що дозволяє декільком блокам в межах однієї функції повторно використовувати імена змінних без перехресного забруднення.

  4. Мови програмування оболонки не мають стандартної бібліотеки.

    Можна стверджувати, що стандартна бібліотека мови сценаріїв оболонок - це вміст PATH, але це просто говорить про те, що для виконання будь-якого наслідку, сценарій оболонки повинен викликати іншу цілу програму, ймовірно, одну, написану більш потужною мовою почати з.

    Не існує також широко використовуваного архіву бібліотек утиліти оболонок, як у CPAN Perl . Без великої доступної бібліотеки стороннього утилітного коду програміст повинен написати більше коду вручну, щоб вона була менш продуктивною.

    Навіть не зважаючи на те, що більшість скриптів оболонок покладаються на зовнішні програми, зазвичай написані на С, щоб зробити щось корисне, є всі витрати pipe()fork()exec()ланцюги викликів. Ця закономірність є досить ефективною в Unix, порівняно з IPC та запуском процесів на інших ОС, але тут вона ефективно замінює те, що ви робите, з викликом підпрограми іншою мовою сценаріїв, що ще набагато ефективніше. Це ставить серйозну шапку на верхню межу швидкості виконання сценарію оболонки.

  5. Сценарії оболонки мають невелику вбудовану здатність підвищувати свою ефективність за допомогою паралельного виконання.

    Bourne снарядів &, waitі трубопроводи для цього, але це в основному використовується тільки для складання кількох програм, а не для досягнення CPU або паралелізм введення / виведення. Ви, швидше за все, не зможете прив’язати ядра або наситити RAID-масив виключно сценарієм оболонок, і, якщо це зробити, ви могли б досягти набагато вищої продуктивності на інших мовах.

    Трубопроводи, зокрема, є слабкими способами підвищення продуктивності за допомогою паралельного виконання. Це дозволяє лише дві програми працювати паралельно, і одна з двох, ймовірно, буде заблокована вводу / виводу з іншого або в інший в будь-який момент часу.

    Є новоявлені шляху навколо цього, такі , як xargs -Pі GNUparallel , але це просто переходить до пункту 4 вище.

    Не маючи вбудованої можливості повною мірою використовувати переваги багатопроцесорних систем, сценарії оболонки завжди будуть повільнішими, ніж добре написана програма мовою, яка може використовувати всі процесори в системі. Щоб знову взяти цей configureприклад сценарію GNU Autoconf , подвоєння кількості ядер у системі мало зробить для покращення швидкості, з якою він працює.

  6. Мови сценаріїв оболонок не мають вказівників чи посилань .

    Це заважає вам робити безліч речей, які легко виконуються іншими мовами програмування.

    По-перше, неможливість опосередкованого посилання на іншу структуру даних в пам'яті програми означає, що ви обмежені вбудованими структурами даних . Ваша оболонка може мати асоціативні масиви , але як вони реалізовані? Існує кілька можливостей, кожен з яких має різний компроміс: червоно-чорні дерева , дерева AVL та хеш-таблиці - найпоширеніші, але є й інші. Якщо вам потрібен інший набір компромісів, ви застрягли, тому що без посилань у вас немає способу вручну розгорнути багато типів розширених структур даних. Ви застрягли в тому, що вам дали.

    Або, можливо, вам потрібна структура даних, яка навіть не має адекватної альтернативи, вбудованої у ваш інтерпретатор сценарію оболонки, наприклад, спрямований ациклічний графік , який може знадобитися для моделювання графіка залежності . Я програмую десятиліттями, і єдиний спосіб, який я можу зробити, щоб це зробити в сценарії оболонки, було б зловживати файловою системою , використовуючи символьні посилання як недолікові посилання. Це таке рішення, яке ви отримуєте, коли покладаєтесь лише на повну цінність Тьюрінга, і це нічого не говорить про те, чи рішення це елегантне, швидке або зрозуміле.

    Розширені структури даних - це лише одне використання для покажчиків та посилань. Існує купа інших програм для них , які просто неможливо зробити легко на мові сценаріїв сім'ї Борна.

Я міг би продовжувати і продовжувати, але я думаю, що ти тут зрозумієш. Простіше кажучи, існує багато більш потужних мов програмування для систем типу Unix.

Це величезна перевага, яка могла б компенсувати посередність самої мови в деяких випадках.

Зрозуміло, і саме тому GNU Autoconf використовує для своїх configureвихідних configureскриптів цілеспрямовано обмежений підмножину сімейства сценаріїв оболонок Bourne : так що його сценарії працюватимуть майже скрізь.

Ви, мабуть, не знайдете більшої групи віруючих у корисності написання високопортативного діалекту оболонок Борна, ніж розробники GNU Autoconf, але їхнє власне творіння написане головним чином на Perl, плюс деякі m4, і лише трохи шкаралупи сценарій; тільки вихід Autoconf - це чистий сценарій оболонки Bourne. Якщо це не ставить питання про те, наскільки корисна концепція "Борн скрізь", я не знаю, що буде.

Отже, чи існує обмеження щодо складності таких програм?

Технічно кажучи, ні, як підказує ваше спостереження за повнотою Тюрінга.

Але це не те саме, що говорити про те, що довільно великі сценарії оболонок приємні для запису, легко налагоджувати або швидко виконувати.

Чи можливо написати, скажімо, компресор / декомпресор файлу в чистому файлі?

"Чистий" Баш, без жодних закликів до речей у PATH? Компресор, ймовірно, піддається виконанню echoта шестигранних послідовностей втечі, але це було б досить болісно. Декомпресор може бути неможливим записати таким чином через неможливість обробки двійкових даних у оболонці . Ви в кінцевому підсумку зателефонуєте на odтаке і таке, щоб перевести двійкові дані у текстовий формат, власний спосіб роботи з оболонкою.

Як тільки ви почнете говорити про використання сценаріїв оболонок так, як це було призначено, як клей для керування іншими програмами в PATH, двері відкриваються, тому що тепер ви обмежуєтесь лише тим, що можна зробити іншими мовами програмування, а це - вам сказати взагалі не мають обмежень. Скрипт оболонки , який отримує весь його силу, викликавши до інших програм в PATHне працює так швидко , як монолітні програми , написаних на більш потужних мовах, але це дійсно працювати.

І в цьому справа. Якщо вам потрібна програма для швидкого запуску, або якщо вона сама по собі повинна бути потужною, а не позичати владу у інших, ви не пишете її в оболонці.

Проста відеоігра?

Ось тетріс в шкаралупі . Інші такі ігри доступні, якщо ви хочете шукати.

є лише дуже обмежені інструменти для налагодження

Я б поставив підтримку інструменту налагодження приблизно на 20-му місці в списку функцій, необхідних для підтримки програмування в цілому. Цілий ряд програмістів значно більше покладаються на printf()налагодження, ніж власні налагоджувачі, незалежно від мови.

У оболонці у вас є echoі set -x, що разом достатньо для налагодження великої кількості проблем.


2
"Сценарії оболонки мають мало вбудованої здатності робити паралельне виконання." На мій погляд, оболонка має кращу підтримку паралельної обробки, ніж більшість інших мов. З одним символом &ви можете запускати процеси паралельно. Ви можете, waitщоб дочірні процеси завершилися. Ви можете налаштувати трубопроводи та більш складні мережі труб, використовуючи названі труби. Найголовніше - це просто зробити паралельну обробку правильним способом, з дуже невеликим кодовим кодовим шаблоном та уникнути ризиків та труднощів багатопотокової пам'яті.
Сем Уоткінс

@SamWatkins: Я оновив пункт 5 вище, щоб вирішити вашу відповідь. Хоча я теж прихильник передачі повідомлень між окремими процесами як спосіб уникнути багатьох проблем, властивих паралелізму спільної пам’яті, я говорив тут про підвищення продуктивності, а не про комбінованість та таке, і про те часто вимагає паралелізму спільної пам'яті.
Warren Young

Сценарії оболонки хороші для складання прототипів - але з часом проект повинен перейти на належну мову програмування, а в ідеалі - на компільовану мову. Тоді в крайніх випадках складання, як ви бачили з проектом FFmpeg. Cmake - хороший приклад того, що має статися з Autotools - його написано на C та не вимагає Perl або Texinfo або M4. Це справді химерність, що Autotools досі так сильно покладається на сценарії оболонки після 30 років wikipedia.org/wiki/GNU_Build_System#Criticism
Стівен Пенні

9

Ми можемо ходити чи плавати куди завгодно, тож чому ми турбуємось на велосипедах, машинах, поїздах, човнах, літаках та інших транспортних засобах? Звичайно, ходьба або плавання можуть бути втомлюючими, але в тому, що не потрібно зайвого обладнання, є величезна перевага.

По-перше, хоча bash є повним Тьюрінгом, він не добре маніпулювати даними, окрім цілих чисел (не надто великих), рядків, (одновимірних) масивів рядків та кінцевих карт від рядків до рядків. Будь-який інший тип даних потребує набридливого кодування, що ускладнює написання програми і часто нав'язує продуктивність, недостатньо хорошу на практиці. Наприклад, операції з плаваючою комою в баші важкі і повільні.

Крім того, bash має дуже мало способів взаємодії зі своїм оточенням. Він може запускати процеси, він може виконувати деякі прості види доступу до файлів (через перенаправлення), і ось про це. Bash також має клієнтського мережевого клієнта. Bash може випромінювати нульові байти досить легко ( printf \\0), але не розбирати нульові байти у своєму введенні, що робить його непридатним для читання двійкових даних. Bash не може безпосередньо робити інші речі: для цього він повинен викликати зовнішні програми. І це нормально: оболонки розроблені для первинної мети запуску зовнішніх програм! Оболонки - це мова клею для поєднання програм разом. Але якщо ви працюєте із зовнішньою програмою, це означає, що програма повинна бути доступною - і тоді ви зменшите перевагу переносимості:).

Bash не має жодної функції, яка полегшує написання надійних програм, крім set -e. У ньому немає (корисних) типів, просторів імен, модулів або вкладених структур даних. Помилки - це труднощі номер один у програмуванні; хоча простота написання програм без помилок не завжди є вирішальним фактором для вибору мови, баш за цим рахунком погано займає позицію. Bash також погано займає продуктивність, коли робить щось інше, ніж поєднання програм разом.

Довгий час bash не працював у Windows, і навіть сьогодні він не присутній в установці Windows за замовчуванням, і він не працює повністю нативно (навіть у WSL) в тому сенсі, що не має інтерфейсів для Народні функції Windows. Bash не працює на iOS і не встановлений за замовчуванням на Android. Тому, якщо ви не пишете програму Unix, програма bash зовсім не є портативною.

Потрібна компілятор - це не проблема для портативності. Компілятор працює на машині розробників. Вимога інтерпретатора або сторонніх бібліотек може бути проблемою, але в Linux це вирішена проблема через дистрибутивні пакети, а під Windows, Android та iOS люди зазвичай постачають сторонні компоненти у свій пакет програм. Тож ви маєте на увазі тип переносимості з портативністю, що не стосується застосувань, що працюють під керуванням.

Моя відповідь стосується снарядів, окрім удару. Кілька деталей відрізняються від оболонки до оболонки, але загальна ідея та ж.


1
Я вважаю, про міф про портативність говорили досить часто, не впевнений, що я б використовував цей предмет як негативний, оскільки він стосується і більшості інших мов, включаючи Java. Навіть у PHP, що працює на сервері Windows проти vs nix-сервера, є деякі невеликі відмінності, про які ви завжди повинні бути обізнані, якщо ви досить дурні, щоб запустити що-небудь на сервері Windows, тобто. Багато речей не працює на android або iOs, тому не впевнені, як це може бути достовірним коментарем.
Лізардкс

7

Деякі причини не використовувати скрипти оболонок для великих програм, просто вгорі голови:

  • Більшість функцій виконується шляхом відключення зовнішніх команд, що відбувається повільно. На відміну від цього, мови програмування, такі як Perl, можуть робити еквівалент mkdirабо grepвнутрішньо.
  • Немає простого способу доступу до бібліотек C або здійснення прямих системних дзвінків, а це означає, що, наприклад, відеоігри було б важко створити
  • Правильні мови програмування мають кращу підтримку складних структур даних. Хоча у Bash є масиви та асоціативні масиви, але я не хотів би думати про пов'язаний список чи дерево.
  • Оболонка створена для обробки команд, які зроблені, якщо текст. Бінарні дані (тобто змінні, що містять байти NUL (байти зі значенням нуля)) важко обробити неможливо. Трохи залежить від оболонки, zshмає деяку підтримку. Це пояснюється тим, що інтерфейс для зовнішніх програм здебільшого є текстовим і \0використовується як роздільник.
  • Також із-за зовнішніх команд розділення між кодом та даними є дещо складним. Свідчіть всі проблеми, які виникають при цитуванні даних до іншої оболонки (тобто під час запуску bash -c ...чи ssh -c ...)

Це найточніший набір негативів для мене, тому що хтось, хто робить багато великих сценаріїв bash, це приблизно буде тим, що я також зазначив би як мінуси. Однак одне, що я виявив, - це те, що Bash насправді не набагато повільніше, ніж інші компільовані мови при порівнянні аналогічних функцій. У мене підкрадається підозра, що я намагаюся написати деякі найскладніші речі, які я маю в ударі в пітон, різниця в швидкості не зробить монстровану роботу ніколи не варте цього. Однак, лише Bash я виявив занадто обмеженим, але Bash + gawk працює добре, gawk майже справжній.
Лізардкс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.