Оболонка - це інтерфейс для операційної системи. Зазвичай це сама по собі більш-менш надійна мова програмування, але з функціями, розробленими для спрощення конкретного взаємодії з операційною системою та файловою системою. Семантика оболонки POSIX (надалі називається просто "оболонка") трохи сумбурна, поєднуючи деякі особливості LISP (s-вирази мають багато спільного з розбиванням слів оболонки ) та C (більша частина арифметичного синтаксису оболонки семантика походить від С).
Інший корінь синтаксису оболонки походить від його виховання як мішанки окремих службових програм UNIX. Більшість того, що часто вбудовано в оболонку, насправді може бути реалізовано як зовнішні команди. Він кидає багато неофітів оболонки за петлю, коли вони усвідомлюють, що /bin/[
існує в багатьох системах.
$ if '/bin/[' -f '/bin/['; then echo t; fi
t
ват?
Це має набагато більше сенсу, якщо поглянути на те, як реалізована оболонка. Ось реалізація, яку я зробив як вправу. Це на Python, але я сподіваюся, що це не затримка ні для кого. Це не дуже надійно, але це повчально:
#!/usr/bin/env python
from __future__ import print_function
import os, sys
'''Hacky barebones shell.'''
try:
input=raw_input
except NameError:
pass
def main():
while True:
cmd = input('prompt> ')
args = cmd.split()
if not args:
continue
cpid = os.fork()
if cpid == 0:
os.execl(args[0], *args)
else:
os.waitpid(cpid, 0)
if __name__ == '__main__':
main()
Я сподіваюся, що вищесказане дає зрозуміти, що модель виконання оболонки в значній мірі:
1. Expand words.
2. Assume the first word is a command.
3. Execute that command with the following words as arguments.
Розширення, вирішення команд, виконання. Вся семантика оболонки пов’язана з однією з цих трьох речей, хоча вона набагато багатша, ніж реалізація, про яку я писав вище.
Не всі команди fork
. Насправді існує декілька команд, які не мають сенсу реалізовуватись як зовнішні (такі, що їм доведеться fork
), але навіть такі часто доступні як зовнішні для суворого дотримання POSIX.
Bash базується на цій основі, додавши нові функції та ключові слова для покращення оболонки POSIX. Він майже сумісний з sh, а bash настільки всюдисущий, що деякі автори сценаріїв проходять роки, не розуміючи, що сценарій насправді не може працювати в строго POSIX-системі. (Мені також цікаво, як люди можуть так піклуватися про семантику та стиль однієї мови програмування, і так мало про семантику та стиль оболонки, але я розходяться.)
Порядок оцінки
Це трохи хитре питання: Bash інтерпретує вирази у своєму первинному синтаксисі зліва направо, але в арифметичному синтаксисі він дотримується пріоритету C. Проте вирази відрізняються від розширень . З EXPANSION
розділу посібника з Bash:
Порядок розкладання: розширення фігурних дужок; розширення тильди, розширення параметрів та змінних, арифметичне розширення та підстановка команд (виконується зліва направо); розбиття слів; та розширення назви шляху.
Якщо ви розумієте розділення слів, розширення імені шляху та розширення параметрів, ви на шляху до розуміння більшості того, що робить bash. Зверніть увагу, що розширення імені шляху, яке відбувається після розбиття слів, є критичним, оскільки воно гарантує, що файл із пробілами в його назві все одно може відповідати глобусу. Ось чому хороше використання розширення глобуса краще, ніж загальний аналіз команд , загалом.
Сфера дії
Сфера функцій
Подібно до старого ECMAscript, оболонка має динамічну область дії, якщо ви явно не оголошуєте імена у функції.
$ foo() { echo $x; }
$ bar() { local x; echo $x; }
$ foo
$ bar
$ x=123
$ foo
123
$ bar
$ …
Навколишнє середовище та "сфера дії"
Підоболонки успадковують змінні своїх батьківських оболонок, але інші типи процесів не успадковують неекспортовані імена.
$ x=123
$ ( echo $x )
123
$ bash -c 'echo $x'
$ export x
$ bash -c 'echo $x'
123
$ y=123 bash -c 'echo $y'
123
Ви можете поєднати ці правила масштабування:
$ foo() {
> local -x bar=123
> bash -c 'echo $bar'
> }
$ foo
123
$ echo $bar
$
Набір тексту
Гм, типи. Ага. Bash дійсно не має типів, і все розширюється до рядка (або, можливо, слово було б більш доречним.) Але давайте розглянемо різні типи розширень.
Струни
Майже що-небудь можна розглядати як струну. Бареслови в bash - це рядки, значення яких повністю залежить від розширення, застосованого до нього.
Без розширення
Можливо, варто продемонструвати, що оголене слово насправді є лише словом, і цитати в цьому нічого не змінюють.
$ echo foo
foo
$ 'echo' foo
foo
$ "echo" foo
foo
Розширення підрядка
$ fail='echoes'
$ set -x
$ "${fail:0:-2}" Hello World
+ echo Hello World
Hello World
Докладніше про розширення читайте у Parameter Expansion
розділі посібника. Це досить потужний.
Цілі числа та арифметичні вирази
Ви можете проникнути імена цілим атрибутом, щоб сказати оболонці обробляти праву частину виразів присвоєння як арифметику. Потім, коли параметр розширюється, він буде оцінюватися як цілочисельна математика перед розширенням до ... рядка.
$ foo=10+10
$ echo $foo
10+10
$ declare -i foo
$ foo=$foo
$ echo $foo
20
$ echo "${foo:0:1}"
2
Масиви
Аргументи та позиційні параметри
Перш ніж говорити про масиви, варто обговорити позиційні параметри. Аргументи сценарію оболонки можна отримати з допомогою пронумерованих параметрів, $1
, $2
, $3
і т.д. Ви можете отримати доступ до всіх цих параметрах одночасно , використовуючи "$@"
, що розкладання має багато спільного з масивами. Ви можете встановити та змінити позиційні параметри, використовуючи set
або shift
вбудовані, або просто викликаючи оболонку або функцію оболонки з цими параметрами:
$ bash -c 'for ((i=1;i<=$#;i++)); do
> printf "\$%d => %s\n" "$i" "${@:i:1}"
> done' -- foo bar baz
$1 => foo
$2 => bar
$3 => baz
$ showpp() {
> local i
> for ((i=1;i<=$#;i++)); do
> printf '$%d => %s\n' "$i" "${@:i:1}"
> done
> }
$ showpp foo bar baz
$1 => foo
$2 => bar
$3 => baz
$ showshift() {
> shift 3
> showpp "$@"
> }
$ showshift foo bar baz biz quux xyzzy
$1 => biz
$2 => quux
$3 => xyzzy
Посібник із Bash також іноді називають $0
позиційним параметром. Я вважаю це заплутаним, оскільки воно не включає його до числа аргументів $#
, але це нумерований параметр, тому meh. $0
- це назва оболонки або поточного сценарію оболонки.
Масиви
Синтаксис масивів моделюється за позиційними параметрами, тому в основному здорово думати про масиви як про іменований вид "зовнішніх позиційних параметрів", якщо хочете. Масиви можна оголосити, використовуючи такі підходи:
$ foo=( element0 element1 element2 )
$ bar[3]=element3
$ baz=( [12]=element12 [0]=element0 )
Ви можете отримати доступ до елементів масиву за індексом:
$ echo "${foo[1]}"
element1
Ви можете нарізати масиви:
$ printf '"%s"\n' "${foo[@]:1}"
"element1"
"element2"
Якщо ви обробляєте масив як звичайний параметр, ви отримаєте нульовий індекс.
$ echo "$baz"
element0
$ echo "$bar"
$ …
Якщо ви використовуєте лапки або зворотні скісні риски для запобігання розділення слів, масив буде підтримувати вказане розбиття слів:
$ foo=( 'elementa b c' 'd e f' )
$ echo "${#foo[@]}"
2
Основною відмінністю масивів від позиційних параметрів є:
- Позиційні параметри не розріджені. Якщо
$12
встановлено, ви також можете бути впевнені $11
, що встановлено. (Можна встановити порожній рядок, але $#
не менше 12). Якщо "${arr[12]}"
встановлено, гарантія "${arr[11]}"
не встановлюється, і довжина масиву може бути такою, як 1.
- Нульовий елемент масиву однозначно є нульовим елементом цього масиву. У позиційних параметрах нульовий елемент - це не перший аргумент , а ім'я оболонки або сценарію оболонки.
- Для
shift
масиву вам потрібно нарізати і перепризначити його, наприклад arr=( "${arr[@]:1}" )
. Ви також можете це зробити unset arr[0]
, але це зробить перший елемент з індексом 1.
- Масиви можна неявно розподіляти між функціями оболонки як глобальні, але вам потрібно явно передавати позиційні параметри функції оболонки, щоб вона бачила їх.
Часто зручно використовувати розширення імен шляху для створення масивів імен файлів:
$ dirs=( */ )
Команди
Команди є ключовими, але вони також розглядаються набагато глибше, ніж я можу в інструкції. Прочитайте SHELL GRAMMAR
розділ. Різні типи команд:
- Прості команди (наприклад
$ startx
)
- Трубопроводи (наприклад
$ yes | make config
) (ха-ха)
- Списки (наприклад
$ grep -qF foo file && sed 's/foo/bar/' file > newfile
)
- Складені команди (наприклад
$ ( cd -P /var/www/webroot && echo "webroot is $PWD" )
)
- Копроцеси (складні, без прикладу)
- Функції (названа складена команда, яку можна розглядати як просту команду)
Модель виконання
Модель виконання, звичайно, включає як купу, так і стек. Це характерно для всіх програм UNIX. Bash також має стек викликів для функцій оболонки, видимий через вкладене використання caller
вбудованого.
Список літератури:
SHELL GRAMMAR
Розділ керівництва Баша
- XCU Shell Command Language документація
- Керівництво Bash на вікі Greycat в.
- Розширене програмування в середовищі UNIX
Будь ласка, коментуйте, якщо хочете, щоб я розширювався далі у певному напрямку.