Bash
Ошибка скрипта: Модуля «hatnote» не существует.{{#if: | }} Шаблон:КарточкаШаблон:Сортировка: по изображениям{{#if:||Шаблон:Категория по дате}}Ошибка скрипта: Модуля «check for unknown parameters» не существует.Шаблон:ИТ:Общие проверки Bash (от англ. Шаблон:Lang-en2, каламбур Шаблон:Lang-en2 — «возрождённый» shell) — усовершенствованная и модернизированная вариация командной оболочки Bourne shell. Одна из наиболее популярных современных разновидностей командной оболочки UNIX. Особенно популярна в среде Linux, где она часто используется в качестве предустановленной командной оболочки.
Представляет собой командный процессор, работающий, как правило, в интерактивном режиме в текстовом окне. Bash также может читать команды из файла, который называется скриптом (или сценарием). Как и все Unix-оболочки, он поддерживает автодополнение имён файлов и каталогов, подстановку вывода результата команд, переменные, контроль над порядком выполнения, операторы ветвления и цикла. Ключевые слова, синтаксис и другие основные особенности языка были заимствованы из sh. Другие функции, например, история, были скопированы из csh и ksh. Bash в основном соответствует стандарту POSIX, но с рядом расширений<ref>Шаблон:Cite web</ref>.
Название «bash» является акронимом от англ. Шаблон:Lang-en2 («ещё-одна-командная-оболочка-Борна») и представляет собой игру слов: Bourne-shell — одна из популярных разновидностей командной оболочки для UNIX (sh), автором которой является Стивен Борн (1978), усовершенствована в 1987 году Брайаном Фоксом. Фамилия Bourne (Борн) перекликается с английским словом Шаблон:Lang-en2, означающим «родившийся», отсюда: рождённая-вновь-командная оболочка.
В сентябре 2014 года в bash была обнаружена широко эксплуатируемая уязвимость Bashdoor.
История
Функциональность сценариев оболочки изначально появилась в файлах, называемых «runcom» в честь появившегося в 1963 году макропроцессора с тем же названием. Суффикс «rc» — это сокращение от «runcom».<ref> Шаблон:Cite web</ref> Термин «оболочка» был придуман Луи Пузеном в 1964 или 1965 году и появился в его статье 1965 года «Оболочка, глобальный инструмент для вызова и цепочки процедур в системе», в которой описываются многие функции, позже обнаруженные во многих оболочках Unix.<ref> Шаблон:Cite web </ref><ref> Шаблон:Cite web </ref> Стандарт ASCII для кодировки символов был определен в 1969 году в документе под названием RFC 20.<ref> Шаблон:Cite web</ref>
Хронология
Ниже перечислены значимые события в истории Bash :
| Дата | Событие |
|---|---|
| 10 января 1988 |
Брайан Фокс начал разрабатывать Bash после того, как Ричард Столлман был недоволен отсутствием прогресса, достигнутого предыдущим разработчиком.<ref name="comp.unix_88"> Шаблон:Cite newsgroup </ref> Столлман и FSF считали, что свободная оболочка, способная запускать существующие сценарии оболочки, настолько важна для полностью свободной системы, построенной на коде BSD и GNU, что это был один из немногих проектов, которые они финансировали сами. Fox выполнял эту работу в качестве сотрудника FSF.<ref name =comp.unix_88/><ref> Шаблон:Cite web </ref> |
| 8 июня 1989 |
Фокс выпустил Bash как бета-версию 0.99.<ref> Шаблон:Cite newsgroup</ref> Лицензия — GPL 1.0+. «Помимо поддержки обратной совместимости для сценариев, Bash включил в себя функции оболочек Korn и C. Вы найдете историю команд, редактирование командной строки, стек каталогов (pushd и popd), множество полезных переменных окружения, автодополнение команд и многое другое.»<ref> Шаблон:Cite web </ref> В конечном итоге, он стал поддерживать «регулярные выражения (аналогично Perl) и ассоциативные массивы». |
| 1991 |
Bash имеет историческое значение как одна из первых программ, портированных на Линусом Торвальдсом, наряду с GNU Compiler (GCC).<ref> Шаблон:Cite newsgroup</ref> |
| 1992 ~ 1994 |
Брайан Фокс ушел с поста основного сопровождающего где-то между серединой 1992 года<ref> Шаблон:Cite newsgroup </ref> и серединой 1994 года.<ref> Шаблон:Cite web </ref><ref> Шаблон:Citation </ref> Его обязанности были переданы другому раннему участнику, Чету Рэйми.<ref name =bash-top/><ref> Шаблон:Multiref2</ref><ref name="computerworld_08"/><ref name="goftw2015"/> С тех пор Bash стал самой популярной интерактивной оболочкой по умолчанию среди основных дистрибутивов GNU/Linux, таких как Fedora, Debian и openSUSE, а также среди их производных и конкурентов.<ref> Шаблон:Cite book </ref><ref> Шаблон:Cite book </ref> |
| 26 января 1994 |
Debian — первый релиз. Bash является оболочкой по умолчанию для интерактивного и неинтерактивного взаимодействия.<ref name="debian.org_shell"> Шаблон:Cite web</ref> |
| 31 декабря 1996 |
Чет Рэйми выпустил bash 2.0. Лицензия — GPL 2.0+. |
| 5 июня 1997 |
Вышел Bash 2.01. |
| 18 апреля 1998 |
Вышел Bash 2.02. |
| 19 февраля 1999 |
Вышел Bash 2.03. |
| 21 марта 2000 |
Вышел Bash 2.04. |
| 14 сентября 2000 |
Список рассылки Bug-bash существует.<ref> Шаблон:Cite mailing list </ref> |
| 9 апреля 2001 |
Вышел Bash 2.05.<ref> Шаблон:Cite mailing list </ref> |
| 2003 |
Начиная с OS X 10.3 Panther, Bash стал оболочкой по умолчанию в операционных системах Apple (то есть macOS).<ref>Essential Mac OS S Panther Server Administration, pg 189 </ref><ref> Шаблон:Cite book </ref> Он также был доступен в OS X 10.2 Jaguar, где оболочкой по умолчанию был tcsh. |
| 27 июля 2004 |
Вышел Bash 3.0.<ref> Шаблон:Cite mailing list </ref> |
| 9 декабря 2005 |
Вышел Bash 3.1.<ref> Шаблон:Cite mailing list </ref> |
| 12 октября 2006 |
Вышел Bash 3.2.<ref> Шаблон:Cite mailing list </ref> Лицензия — GPL 2.0+. |
| 2006 |
В Ubuntu в качестве оболочки по умолчанию вместо bash используется dash. |
| 20 февраля 2009 |
Вышел Bash 4.0<ref> Шаблон:Cite mailing list </ref> Лицензия — GPL 3.0+. |
| 2 января 2010 |
Вышел Bash 4.1.<ref> Шаблон:Cite mailing list </ref> |
| 14 февраля 2011 |
Вышел Bash 4.2.<ref> Шаблон:Cite mailing list </ref> |
| 2012 |
В Solaris 11, «оболочкой пользователя по умолчанию является Bourne-again (bash) shell.»<ref> Шаблон:Cite web </ref> |
| 27 февраля 2014 |
Вышел Bash 4.3.<ref> Шаблон:Cite mailing list </ref> |
| 8 сентября 2014 |
Bashdoor (Shellshock).<ref> Шаблон:Cite web </ref><ref> Шаблон:Cite web </ref> Патчи для исправления ошибок были выпущены вскоре после того, как ошибки были обнаружены.<ref> Шаблон:Cite mailing list </ref> |
| 2015 |
Termux и другие приложения для эмуляции терминала обеспечивают доступность Bash на Android. |
| 15 сентября 2016 |
Вышел Bash 4.4. |
| 2009 ~ 2018 |
Apple отказывается принять четвёртую версию Bash, распространяемую под третьей версией лицензии GNU GPL, и перестаёт поставлять обновления для Bash после версии 3.2 (поставляемой в MacOS Mojave). |
| 5 июня 2019 |
Apple объявляет zsh своей оболочкой по умолчанию<ref> Шаблон:Cite web </ref> и поставляет версию 5.7 в своей MacOS Catalina.<ref> Шаблон:Cite web </ref><ref> Шаблон:Cite web </ref><ref> Шаблон:Cite web </ref> |
| 7 января 2019 |
Вышел Bash 5.0.<ref> Шаблон:Cite mailing list </ref> |
| 7 декабря 2020 |
Вышел Bash 5.1.<ref> Шаблон:Cite mailing list </ref> |
| 26 сентября 2022 |
Вышел Bash 5.2. |
| 2025 |
Вышел Bash 5.3. |
Различия в синтаксисе
Подавляющее большинство важных скриптов командного процессора Bourne может выполняться без изменения в bash, за исключением тех, которые ссылаются на специальные переменные Bourne или используют встроенные команды Bourne. Синтаксис команд Bash включает идеи, заимствованные у Korn shell (ksh) и C shell (csh), такие как редактирование командной строки, история команд, стек каталогов, переменные $RANDOM и $PPID, синтаксис замены команды $(…). Когда Bash используется как интерактивный командный процессор, он поддерживает автозавершение имён программ, файлов, переменных и т. п. с помощью клавиши Шаблон:Клавиша.
Внутренние команды
Интерпретатор bash имеет множество встроенных команд, часть из которых имеет аналогичные исполняемые файлы в операционной системе. Однако следует обратить внимание, что чаще всего для встроенных команд отсутствуют man-страницы, а при попытке просмотра справки по встроенной команде на самом деле будет выдаваться справка по исполняемому файлу. Исполняемый файл и встроенная команда могут различаться параметрами. Информация по встроенным командам расписана в справочной странице bash:
man bash
| Ввод-вывод | |
|---|---|
echo |
выводит выражение или содержимое переменной (stdout), но имеет ограничения в использовании<ref>Невозможно вывести значение переменной, содержащей -n (в отличие от printf -- '%s\n' "${переменная}"), так как -n интерпретируется как параметр команды echo.</ref>
|
printf |
команда форматированного вывода, расширенный вариант команды echo
|
read |
«читает» значение переменной со стандартного ввода (stdin), в интерактивном режиме это клавиатура |
| Файловая система | |
cd |
изменяет текущий каталог |
pwd |
выводит название текущего рабочего каталога (от англ. Шаблон:Lang-en2) |
pushd |
изменяет текущий каталог с возможностью возврата в обратном порядке |
popd |
возвращает текущий каталог после pushd
|
dirs |
выводит или очищает содержимое стека каталогов, сохранённых через pushd
|
| Действия над переменными | |
let |
производит арифметические операции над переменными |
eval |
транслирует список аргументов из списка в команды |
set |
изменяет значения внутренних переменных скрипта |
unset |
удаляет переменную |
export |
экспортирует переменную, делая её доступной дочерним процессам |
declare, typeset |
задают и/или накладывают ограничения на переменные |
getopts |
используется для разбора аргументов, передаваемых скрипту из командной строки |
| Управление сценарием | |
source, . (точка) |
запуск указанного сценария |
exit |
безусловное завершение работы сценария |
exec |
заменяет текущий процесс новым, запускаемым командой exec
|
shopt |
позволяет изменять ключи (опции) оболочки «на лету» |
| Команды | |
true |
возвращает код завершения ноль (успешное завершение) |
false |
возвращает код завершения, который свидетельствует о неудаче |
type prog |
выводит полный путь к prog |
hash prog |
запоминает путь к prog |
help COMMAND |
выводит краткую справку по использованию внутренней команды COMMAND |
| Управление запущенными в командной оболочке задачами | |
jobs |
показывает список запущенных в командной оболочке задач либо информацию о конкретной задаче по её номеру |
fg |
переключает поток ввода на текущую задачу (или на определённую задачу, если указан её номер) и продолжает её исполнение |
bg |
продолжает исполнение текущей приостановленной задачи (или определённых задач, если указаны их номера) в фоновом режиме |
wait |
ожидает завершения указанных задач |
Скрипты
В простейшем случае скрипт — простой список команд, записанный в файл. Командный процессор должен знать, что он должен этот файл обработать, а не просто прочесть его содержимое. Для этого служит специальная конструкция, называемая shebang: #!. Символ # задаёт комментарий, но в данном случае shebang означает, что после этого спецсимвола находится путь к интерпретатору для исполнения сценария.
Синтаксис
Синтаксис команд bash — это расширенный синтаксис команд Bourne shell. Окончательная спецификация синтаксиса команд bash есть в Bash Reference Manual, распространяемом проектом GNU.<ref>Bash Reference Manual Шаблон:Wayback.</ref>
«Hello world»
<source lang="bash">
- !/usr/bin/env bash
echo 'Hello World!' </source>
Этот скрипт содержит только две строки. Первая строка сообщает системе о том, какая программа используется для запуска файла. Вторая строка — это единственное действие, которое выполняется этим скриптом, он собственно печатает «Hello world!» в терминале.
Запуск скрипта
Для того, чтобы скрипт стал исполняемым, могут быть использованы следующие команды: <source lang="bash"> chmod +rx scriptname # выдача прав на чтение/исполнение любому пользователю chmod u+rx scriptname # выдача прав на чтение/исполнение только «владельцу» скрипта </source>
Из соображений безопасности путь к текущему каталогу . не включён в переменную окружения $PATH. Поэтому для запуска скрипта необходимо явно указывать путь к текущему каталогу, в котором находится скрипт:
<source lang="bash">
./scriptname
</source>
Кроме того, передать такой файл на исполнение интерпретатору Bash можно и явно, используя команду bash:
<source lang="bash"> bash scriptname </source>
В этом случае не требуется ни установка прав доступа, ни использование последовательности #! в коде.
Перенаправление ввода-вывода
Шаблон:См. также В bash есть встроенные файловые дескрипторы: 0 (stdin), 1 (stdout), 2 (stderr).
- stdin — стандартный ввод — то, что набирает пользователь в консоли;
- stdout — стандартный вывод программы;
- stderr — стандартный вывод ошибок.
Для операций с этими и пользовательскими дескрипторами существуют специальные символы: > (перенаправление вывода), < (перенаправление ввода). Символы &, - могут предварять номер дескриптора; например, 2>&1 — перенаправление дескриптора 2 (stderr) в дескриптор 1 (stdout).
0<filename или <filename |
Перенаправление ввода из файла filename
|
1>filename или >filename |
Перенаправление вывода в файл «filename». Если отключена опция noclobber, то файл перезаписывается поступающими данными
|
1>|filename или >|filename |
Перенаправление вывода в файл «filename», файл перезаписывается поступающими данными |
1>>filename или >>filename |
Перенаправление вывода в файл «filename», данные добавляются в конец файла. При отсутствии файла он создаётся |
2>filename |
Перенаправление стандартного вывода ошибок в файл «filename» |
2>>filename |
Перенаправление стандартного вывода ошибок в файл «filename», данные добавляются в конец файла. При отсутствии файла он создаётся |
&>filename |
Перенаправление вывода и ошибок в файл «filename» |
2>&1 |
Перенаправление вывода ошибок на стандартный вывод |
Bash имеет индивидуальный синтаксис перенаправления, который не поддерживается в Bourne shell. Пример одновременного перенаправления стандартного вывода и стандартных ошибок: <source lang="bash"> command &> file </source> Это проще набрать, чем эквивалентную команду в синтаксисе Bourne shell <source lang="bash"> command > file 2>&1 </source>
Перенаправление «из кода скрипта»
Поддерживается heredoc-синтаксис:
- С интерпретацией переменных и конструкций языка внутри блока:
<source lang="bash"> $a='многострочный' command <<MYDOC123 $a текст $(<$HOME/my_file.txt) MYDOC123 </source>
- Без интерпретации переменных:
<source lang="bash"> command <<'PERLCODE' my $first='Hello'; my $second='world'; say join(', ',$first,$second),'!'; PERLCODE </source>
- С удалением начальных отступов (поддерживаются только отступы табуляцией):
<source lang="bash"> command <<-'TABSTRIP'
for ((i=0; i<10; i++))
do
echo "$i"
done
TABSTRIP </source>
Начиная с версии 2.05b, bash может перенаправлять стандартный ввод из строки, используя следующий синтаксис «here strings»: <source lang="bash"> command <<< "string to be read as standard input" </source>
Если строка содержит пробелы, её следует заключить в кавычки или апострофы, либо экранировать пробелы обратной косой чертой.
Будьте внимательны: вводимая с использованием here strings строка содержит неявную последовательность завершения строки: либо 1 дополнительный байт line feed, либо 2 дополнительных байта: carriage return и line feed. <source lang=bash> cat -<<<'123' | wc -c
- Результат: 4
cat <(echo -n '123') | wc -c
- Результат: 3
wc -c <<<'123'
- Результат: 4
echo -n 123 | wc -c
- Результат: 3
</source>
Начиная с версии 4.1<ref>Шаблон:Cite web</ref> стало возможным указывать в одной строке терминирующую последовательность символов и, сразу за ней, — скобку. Это может быть полезно для присвоения переменной содержимого here-doc: <source lang=bash> var=$( cat -<<'TERMSEQ' Февраль. Достать чернил и плакать! Писать о феврале навзрыд, Пока грохочущая слякоть Весною чёрною горит. TERMSEQ) </source> При этом BASH будет сгенерировано предупреждающее сообщение.
Перенаправление для процесса в целом
Пример (перенаправление стандартного вывода в файл, запись данных, закрытие файла, сброс stdout): <source lang="bash">
- make Filedescriptor(FD) 6 a copy of stdout (FD 1)
exec 6>&1
- open file "test.data" for writing
exec 1>test.data
- produce some content
echo "data:data:data"
- close file "test.data"
exec 1>&-
- make stdout a copy of FD 6 (reset stdout)
exec 1>&6
- close FD6
exec 6>&- </source>
Открытие и закрытие файлов: <source lang="bash">
- open file test.data for reading
exec 6<test.data
- read until end of file
while read -u 6 dta do
echo "$dta"
done
- close file test.data
exec 6<&- </source>
Подстановка вывода команд
Захват вывода внешних команд: <source lang="bash">
- выполнить 'date' и поместить результат в VAR
VAR="$(date)" echo "$VAR" #выведет дату на момент вызова предыдущей строки </source>
При этом не поддерживаются строки с нуль-символом, заключительные переводы строк пропадают, в отличие от каналов и прочего файлового ввода-вывода.
Условный оператор
<source lang="bash">
- !/usr/bin/env bash
T1='foo' T2='bar' if $T1 == "$T2" then
echo 'условие выполняется'
else
echo 'условие не выполняется'
fi </source> Обратите внимание, что кавычки вокруг левой части необязательны.<ref>BashGuide/TestsAndConditionals Шаблон:Wayback — Greg’s Wiki.</ref>
Циклы
<source lang="bash">
- !/usr/bin/env bash
for i in "Номер "{1..10} do
echo "$i"
done </source>
<source lang="bash" line="1">
- !/usr/bin/env bash
COUNTER=0 while $COUNTER -lt 10 # До тех пор, пока условие ИСТИННО (возвращает 0) выполнять тело цикла do
echo The counter is $COUNTER let COUNTER=COUNTER+1
done </source>
<source lang="bash">
- !/usr/bin/env bash
i=0 until $i -eq 10 # До тех пор, пока условие ЛОЖНО (возвращает не 0) выполнять тело цикла do
echo "$i" i=$(($i+1))
done </source>
<source lang="bash">
- !/usr/bin/env bash
- внутри двойных круглых скобок переменные можно писать в Си-стиле (без $ и разделяя пробелом операторы и операнды)
for (( i = 0; i < 10; i++ )) do echo -n "$i; " done </source>
Массивы
В bash реализованы только одномерные массивы. Индексами могут быть как целочисленные значения («обычный» массив), так и строки (ассоциативный массив, или «хеш»).
Несмотря на унификацию синтаксиса для работы с обычными и ассоциативными массивами, первые не являются разновидностью вторых ни с точки зрения конечного пользователя, ни с точки зрения внутренней логики самого интерпретатора.
Как естественное следствие поддержки «ассоциативных» ключей, в bash существует крайне ограниченная возможность эмуляции многомерных массивов.
Общие операции
Получить значение элемента как индексированного, так и ассоциативного массива можно:
- По фиксированному значению индекса в линейном массиве или строки в ассоциативном:
<source lang="bash"> declare -a arrLinear=(11 22 33 44) declare -A arrAssoc=([city]='London' [index]='420224' ) vi=${arrLinear[3]} # vi=44 va=${arrAssoc['city']} # va='London' </source>
- По значению переменной, при этом для индексированных массивов необязательно указывать сигил ($) перед именем переменной, используемой для индесации. Сигил перед переменной индекса обязателен в случае с ассоциативными массивами:
<source lang="bash"> declare -i n=3 vi=${arrLinear[$i]} # vi=44 vi=${arrLinear[i]} # аналогично предыдущему, но без сигила
declare index='city' va=${arrAssoc[$index]} # va='London', и это правильно va=${arrAssoc[index]} # va='420224', и это ошибочный результат, если подразумевалось [$index] va=${arrAssoc['index']} # va='420224' </source>
Получить строку, состоящую из всех<ref name="массивы с пробелами">Для получения действительно всех элементов, включая элементы из пробелов и пустые строки, обязательно использовать кавычки.</ref> элементов массива, разделённых пробелом. <source lang="bash"> values="${arr[@]}" </source>
Получить строку со всеми<ref name="массивы с пробелами"/> индексами или ключами массива, независимо от того, численные они или текстовые: <source lang="bash"> keys="${!arr[@]}" </source>
Осуществить массовую замену первого вхождения подстроки «MAY» на «MARCH» во всех<ref name="массивы с пробелами"/> элементах массива и вернуть строку, составленную из результирующих элементов массива, разделённых пробелом: <source lang="bash"> values="${arr[@]/MAY/MARCH}" </source> То же самое, но в каждом<ref name="массивы с пробелами"/> элементе массива будут заменены все вхождения подстроки «MAY»: <source lang="bash"> values="${arr[@]//MAY/MARCH}" </source>
До сих пор не все заявленные в документации возможности правильно работают для массивов. Например: <source lang="bash">[ -v 'имя_массива' ]</source> неправильно срабатывает для пустого массива.<ref>Здесь апострофы употреблены правильно.</ref>
Операции с индексированными массивами
Bash имеет поддержку одномерных массивов. Инициализировать элементы массива можно в виде: my_array[xx]. Также можно явно объявить массив в сценарии, с помощью директивы declare:
<source lang="bash">
declare -a my_array
</source>
Обращаться к отдельным элементам массива можно с помощью фигурных скобок: "${my_array[xx]}".
Инициализировать индексированный массив можно двумя способами:
1) <source lang="bash"> Array=(element1 element2 element3) </source> 2) <source lang="bash"> temp_array[0]=element1 temp_array[5]=element temp_array[9]=elementN </source>
Добавление элементов в конец индексированного массива: <source lang="bash"> declare -a arrAnimals arrAnimals=(dog elephant horse cow fox koala turtle)
- Выводим содержимое массива:
echo "${arrAnimals[@]}"
- В конец массива arrAnimals добавляем новый элемент: "pig"
arrAnimals+=(pig)
- И снова показываем содержимое массива:
echo "${arrAnimals[@]}" </source>
Получить первый элемент массива: <source lang="bash"> echo "${arrAnimals[0]}" </source>
Как индексированные, так и ассоциативные массивы поддерживают так называемые «срезы»: <source lang="bash">
- Ещё один способ получить первый элемент массива
echo "${arrAnimals[@]:0:1}"
- Вывести 3 элемента массива, начиная со 2-го:
echo "${arrAnimals[@]:2:3}" </source> Примечание: в случае с ассоциативными массивами порядок следования значений в общем случае будет случайным (определяется используемой хеш-функцией), поэтому операция среза на ассоциативном массиве хотя и допустима, но имеет крайне ограниченную область практического применения.
В массивах с числовыми индексами допустима «обратная» («негативная») адресация, позволяющая получить доступ к элементам, начиная с конца массива: <source lang="bash">
- Значение последнего элемента массива
echo "${arrAnimals[-1]}"
- Значение предпоследнего элемента массива
echo "${arrAnimals[-2]}"
- ... и т.д.
</source>
С помощью встроенной команды mapfile (синоним: readarray) можно отображать содержимое текстового файла в индексированный массив:
<source lang="bash">
declare -a fileWithLogins
mapfile fileWithLogins </etc/passwd
for ((i=0; i<${#fileWithLogins[@]}; i++))
do
echo "Line #$i: ${fileWithLogins[$i]}"
done
</source>
Операции с ассоциативными массивами
Начиная с версии 4.0 в bash появилась поддержка ассоциативных массивов (так называемых hash-массивов).
Для объявления ассоциативных массивов используется ключ -A встроенной команды declare:
<source lang="bash">
declare -A hshArray
</source>
Как и в случае с индексированными массивами, для заполнения ассоциативных массивов могут использоваться два различных синтаксиса:
1) <source lang="bash"> TheCapitalOf[Georgia]='Tbilisi' TheCapitalOf[Australia]='Canberra' TheCapitalOf[Pakistan]='Islamabad' </source> 2) <source lang="bash"> TheCapitalOf=([Georgia]='Tbilisi' [Australia]='Canberra' [Pakistan]='Islamabad') </source>
С помощью ассоциативных массивов можно имитировать поддержку многомерных массивов: <source lang="bash"> declare -A a # объявляет ассоциативный массив 'a' i=1; j=2 # инициализация нескольких индексов a[$i,$j]=5 # присвоение значения "5" в ячейку "$i,$j" (т.е. "1,2") echo ${a[$i,$j]} # вывод хранимых значений из "$i,$j" </source>
К сожалению, в текущих версиях bash скопировать содержимое одного ассоциативного массива в другой простым присваиванием невозможно. Данную проблему можно только обойти, для чего существуют два принципиально разных подхода:
1) Эмулировать объявление второго хеша «с нуля» путём копирования объявления первого хеша: <source lang="bash"> declare -A hsh1 hsh1=(...)
source <(declare -p hsh1 | sed 's/^declare -A hsh1/declare -A hsh2/') </source>
2) Перебрать все<ref name="массивы с пробелами"/> элементы хеша-источника и скопировать их в целевой хеш: <source lang="bash"> declare -A hsh1 hsh2 hsh1=(...)
for k in "${!hsh1[@]}" do hsh2["$k"]="${hsh1["$k"]}" done </source>
Безопасная работа с массивами
Массивы являются удобным способом передачи динамически сгенерированных аргументов в функции или команды. При этом каждый элемент массива представляет собой отдельный аргумент.
<syntaxhighlight lang="bash">
args=( ls -l -a / )
ls "${args[@]}" # ls -l -a /
ls ${args[@]} # ls -l -a /
ls ${args[*]} # ls -l -a /
</syntaxhighlight>
Однако при подобном использовании массивов следует быть осторожным ввиду особенностей раскрытия массивов при использовании в качестве индексов специальных символов @ и *.
Если массив заключён в кавычки и раскрывается с помощью индекса @, то образуется строка из слов, где каждое слово — отдельный элемент массива. Однако если не заключить раскрытие массива в двойные кавычки, то может получиться совсем другой результат: пробелы, входящие в состав элементов массива, становятся разделителями слов.<syntaxhighlight lang="bash">
args=( -l -a '/home/user/Рабочий стол' )
ls "${args[@]}" # просмотр содержимого каталога '/home/user/Рабочий стол'
ls ${args[@]} # ошибка: каталогов '/home/user/Рабочий' и './стол' не существует
</syntaxhighlight>
Массив с индексом *, заключённый в двойные кавычки, раскрывается в одну строку, соединяя все элементы массива через разделители, хранимые в переменной IFS. Если не указать двойные кавычки, то массив раскрывается аналогично раскрытию при использовании индекса @.
<syntaxhighlight lang="bash">
args=( usr local bin )
IFS='/' ls "${args[*]}" # просмотр содержимого каталога '/usr/local/bin'
IFS='/' ls ${args[*]} # поочерёдный просмотр содержимого каталогов '.', './usr', './local' и './bin'
</syntaxhighlight>
При создании массива из содержимого текстовой переменной необходимо учитывать возможное наличие пробелов в тексте. Например, в качестве одного из способов создания массива из строки с перечислением можно рассматривать замену символа-разделителя на пробел. Однако наличие пробелов в разделяемых элементах приведёт к дроблению самих элементов на части.
<syntaxhighlight lang="bash">
array=( ${text//,/} ) # неправильно: текст в переменной 'text' мог содержать пробельные символы
IFS=',' array=( $text ) # правильно: раздельтельным символом является запятая. IFS=',' read -r -a array <<< "$text" # правильно (альтернативный вариант с перенаправлением содержимого переменной 'text' в команду 'read') </syntaxhighlight> К ошибкам может привести создание массива из текста, полученного в качестве вывода команд или из файла, так как не только переводы строк, но также и пробелы с символами табуляции по умолчанию будут считаться разделителями<ref>Шаблон:Cite web</ref>. <syntaxhighlight lang="bash"> array=( "$(ls)" ) # неправильно: пробелы в названии файлов могут разбить имя файла на два элемента массива
IFS=$'\n' array=( "$(ls)" ) # правильно: разделителем будет только перевод строк mapfile -t array < <(ls) # правильно: альтернативный вариант через встроенную команду mapfile </syntaxhighlight>
Конвейер
Конвейер передаёт вывод предыдущей команды на ввод следующей или на вход командного интерпретатора. Метод часто используется для связывания последовательности команд в единую цепочку. Конвейер обозначается символом вертикальной черты |.
Пример (grep работает как фильтр для стандартного вывода): <source lang="bash"> cat filename | grep pattern </source>
Логические операции
Логическое ИЛИ обозначается как ||. В операциях проверки условий оператор || возвращает 0 (success), если один из операндов имеет значение true (ИСТИНА).
Логическое И обозначается как &&. В операциях проверки условий оператор && возвращает 0 (success) тогда и только тогда, когда оба операнда имеют значение true (ИСТИНА).
Примечание: Возможная путаница в понимании возникает из-за того, что команда true (и успешное завершение любой команды) завершается с кодом возврата 0, success (false, наоборот, не 0), в то время как логическое значение true/false — отлично от нуля/равно нулю.
Примеры: <source lang="bash"> if false; then echo "successfully"; else echo "unsuccessfully"; fi unsuccessfully </source>
<source lang="bash"> let "a=(( 1 && 0 && 123))"; echo $?; echo $a 1 # Код завершения команды (нулевой результат вычисления арифметического выражения) 0 # Значение переменной "a", результат логической операции if (( 1 && 0 && 123)); then echo "true"; else echo "false"; fi false
let "a=(( 1 && 123 && -345 ))"; echo $?; echo $a 0 # Код завершения команды 1 # Значение переменной "a", результат логической операции if (( 1 && 123 && -345 )); then echo "true"; else echo "false"; fi true </source>
<source lang="bash"> false && echo "Успешное завершение" || echo "Неуспешное завершение" Неуспешное завершение </source>
Целочисленная математика
Bash может выполнять целочисленные вычисления внутри процесса, используя команду ((…)) и синтаксис переменной $((…))<ref>Устаревший вариант: $[…].</ref>, как показано ниже:
<source lang="bash">
VAR=55 # Устанавливаем переменную VAR, равной 55
((VAR = VAR + 1)) # Добавляем единицу к переменной VAR. Обратите внимание на отсутствие знака '$'
((VAR+=1)) # Сокращённая форма записи инкремента
((++VAR)) # Другой способ увеличения VAR на единицу. Выполняет префиксный инкремент
((VAR++)) # Другой способ увеличения VAR на единицу. Выполняет постфиксный инкремент
echo $((VAR * 22)) # Умножаем VAR на 22 и передаем результат команде
echo $[VAR * 22] # Устаревший способ сделать то же
((VAR<<3)) # Побитовый сдвиг влево (то же, что VAR*8)
((VAR>>3)) # Побитовый сдвиг вправо (то же, что VAR/8)
</source>
Команда ((…)) также может использоваться в условных утверждениях, так как её исходный параметр — это 0 или 1, которые могут интерпретироваться как true или false:
<source lang="bash">
if ((VAR == Y * 3 + X * 2))
then
echo Yes
fi ((Z > 23)) && echo Yes </source>
Команда ((…)) поддерживает следующие операторы сравнения: == != > < >= <=.
Bash не поддерживает вычисления внутри процесса с числами с плавающей точкой. Только командные процессоры Unix Korn shell (версия 1993 года) и zsh (начиная с версии 4.0) поддерживают эту возможность.
Список математических операций: +, -, *, /, ** (возведение в степень), % (деление по модулю, остаток от деления), let — позволяет использовать сокращения арифметических команд (сокращая количество используемых переменных; например: a += b эквивалентно a = a + b и т. п.).
Переменные и аргументы
Аргументы:
$$ |
pid текущего shell (самого процесса-сценария) |
$! |
pid последнего процесса в фоновом режиме |
$? |
код возврата последнего процесса (функции или скрипта) |
$x |
где x — номер параметра, переданного скрипту ($1, $2 и т. д., $0 — последний запущенный скрипт)
|
$# |
количество аргументов командной строки |
$* |
все<ref name="массивы с пробелами"/> аргументы в виде одной строки (слова) |
$@ |
то же самое, что и $*, но при этом каждый<ref name="массивы с пробелами"/> параметр представлен как отдельная строка (слово)
|
$- |
список флагов, переданных сценарию |
$_ |
содержит последний аргумент предыдущей команды |
Встроенные переменные:
$BASH |
путь к исполняемому файлу bash |
$BASHPID |
PID текущего bash * (см. Примечание) |
$BASH_VERSINFO[n] |
массив, состоящий из 6 элементов, содержащий информацию о версии bash |
$BASH_VERSION |
версия Bash, установленного в системе |
$DIRSTACK |
содержимое вершины стека каталогов |
$EDITOR |
заданный по умолчанию редактор |
$EUID |
«эффективный» идентификационный номер пользователя (Effective User ID) |
$FUNCNAME |
имя текущей функции |
$GLOBIGNORE |
перечень шаблонных символов, которые будут проигнорированы при выполнении подстановки имён файлов (globbing) |
$GROUPS |
группы, к которым принадлежит текущий пользователь |
$HOME |
домашний каталог пользователя |
$HOSTNAME |
сетевое имя хоста |
$HOSTTYPE |
тип машины (идентифицирует аппаратную архитектуру) |
$IFS |
разделитель полей во вводимой строке |
$LC_COLLATE |
задаёт порядок сортировки символов, в операциях подстановки имён файлов и в поиске по шаблону |
$LC_CTYPE |
определяет кодировку символов |
$LINENO |
Номер строки исполняемого сценария |
$MACHTYPE |
аппаратная архитектура |
$OLDPWD |
прежний рабочий каталог |
$OSTYPE |
тип операционной системы |
$PATH |
путь поиска (включает в себя каталоги /usr/bin/, /usr/X11R6/bin/, /usr/local/bin и т. д.)
|
$PIPESTATUS |
Код возврата канала (конвейера) |
$PPID |
PID (идентификатор) родительского процесса |
$PS1 |
приглашение командной строки |
$PS2 |
вторичное приглашение командной строки, выводится тогда, когда от пользователя ожидается дополнительный ввод. Обычно отображается как «>» |
$PS3 |
третичное приглашение, выводится, когда пользователь должен сделать выбор в операторе select
|
$PS4 |
приглашение четвёртого уровня, выводится (в изменённом виде) в начале каждой строки отладочного вывода тогда, когда сценарий вызывается с ключом -x. Обычно отображается как «+», «++» и т. д.
|
$PWD |
рабочий (текущий) каталог |
$REPLY |
переменная по умолчанию, куда записывается ввод пользователя, выполненный с помощью команды read
|
$SECONDS |
время работы сценария (в секундах) |
$SHELLOPTS |
список допустимых опций интерпретатора (доступна только для чтения) |
$SHLVL |
уровень вложенности shell |
- Примечание: отличается от $$ при определенных обстоятельствах, таких как подоболочки, которые не требуют повторной инициализации bash.
Пример:
$ echo $(echo $BASHPID $$) $$ $BASHPID
25680 16920 16920 16920
# | | | |
# | | | -- $BASHPID outside of the subshell
# | | -- $$ outside of the subshell
# | -- $$ inside of the subshell
# -- $BASHPID inside of the subshell
Регулярные выражения внутри процесса
Bash 3.0 поддерживает встроенные регулярные выражения с синтаксисом, подобным синтаксису Perl:
<source lang="bash">
string =~ regex
</source>
Синтаксис регулярных выражений задокументирован на страницах документации man 7 regex. Статус выхода устанавливается в 0, если регулярное выражение совпало со строкой, и 1, если нет. Значение подвыражения, заключённого в скобки, можно получить<ref name="массивы с пробелами"/> через переменную ${BASH_REMATCH[@]}, например:
<source lang="bash">
REGEXP='foo(bar)bl(.*)'
if "abcfoobarbletch" =~ $REGEXP
then
echo "Регулярное выражение совпало со строкой!"
echo "$BASH_REMATCH" # выводит: foobarbletch
echo "${BASH_REMATCH[1]}" # выводит: bar
echo "${BASH_REMATCH[2]}" # выводит: etch
fi </source>
Встроенные регулярные выражения обычно работают быстрее, чем выполнение внешней команды grep, потому что соответствующее регулярное выражение выполняется в рамках процесса bash. Если регулярное выражение или строка содержат пробелы или метасимволы (такие как * или ?), их следует взять в кавычки. Рекомендуется использовать переменную для хранения регулярного выражения, как в вышеприведённом примере, для избежания проблем с экранированием специальных символов. Можно использовать вывод bash с опцией -x для проверки, как именно bash воспринимает ваше регулярное выражение.
Расширение скобок
Возможность расширения скобок заимствована у csh. Она позволяет произвольной строке быть сформированной с использованием похожей техники, как это делается с названиями файлов. Однако в bash сгенерированные строки не обязаны быть именами файлов. Результат каждого расширения строки не сортируется, сохраняется порядок слева направо: <source lang="bash">
- Это особенность присущая bash
echo a{p,c,d,b}e # ape ace ade abe </source>
Не следует использовать эту особенность, если скрипт планируется портировать, потому что в традиционных скриптах расширение строки не будет действовать: <source lang="bash">
- Традиционная оболочка не даёт тот же результат
echo a{p,c,d,b}e # a{p,c,d,b}e </source>
Когда используется расширение скобок в сочетании с символами подстановки, скобки раскрываются первыми, а затем результат обрабатывается в обычном режиме. Таким образом, список JPEG- и PNG-изображений в текущем каталоге может быть получен так: <source lang="bash"> ls *.{jpg,jpeg,png} # скобки раскрываются до *.jpg *.jpeg *.png, после чего выполняется поиск по маскам </source>
Горячие клавиши
- Шаблон:Клавиша: Автодополнение строки за курсором.
- Шаблон:Клавиша Шаблон:Клавиша: Повторить последнюю команду.
- Шаблон:Клавиша: Перемещает курсор в начало строки (эквивалентно клавише Шаблон:Клавиша).
- Шаблон:Клавиша: Перемещает курсор на один символ назад (эквивалентно клавише Шаблон:Клавиша).
- Шаблон:Клавиша: Поиск по набранным ранее командам.
- Шаблон:Клавиша: Посылает сигнал SIGINT текущему заданию, который обычно (но не всегда) прекращает и закрывает его.
- Шаблон:Клавиша: Посылает маркер EOF, который (если не отключено опцией и текущая строка не содержит текста) закрывает текущую оболочку (эквивалентно команде exit), при вводе в исполняющуюся программу завершает ввод, если в строке нет текста, иначе завершает строку без символа завершения строки.
- Шаблон:Клавиша: Удаляет текущий символ (только если есть текст на текущей строке) (эквивалентно клавише Шаблон:Клавиша).
- Шаблон:Клавиша: Перемещает курсор в конец строки (эквивалентно клавише Шаблон:Клавиша).
- Шаблон:Клавиша: Перемещает курсор на один символ вперёд (эквивалентно клавише Шаблон:Клавиша).
- Шаблон:Клавиша: Удаляет предыдущий символ (то же самое, что и клавиша Шаблон:Клавиша).
- Шаблон:Клавиша: Эквивалентно клавише Шаблон:Клавиша.
- Шаблон:Клавиша: Эквивалентно клавише Шаблон:Клавиша.
- Шаблон:Клавиша: Очищает содержимое строки после курсора и копирует это в буфер обмена экземпляра bash (kill ring).
- Шаблон:Клавиша: Очищает содержимое экрана (эквивалентно команде
clear). - Шаблон:Клавиша: Эквивалентно клавише Шаблон:Клавиша.
- Шаблон:Клавиша: (следующее) перелистывает к следующей команде (эквивалентно клавише Шаблон:Клавиша).
- Шаблон:Клавиша: Выполняет найденную команду в истории, и выбирает следующую строку относительно текущей строки в истории для того, чтобы отредактировать.
- Шаблон:Клавиша: (предыдущее) перелистывает к предшествующей команде (эквивалентно клавише Шаблон:Клавиша).
- Шаблон:Клавиша: Возобновить вывод процесса.
- Шаблон:Клавиша: Приостановить вывод процесса (stop).
- Шаблон:Клавиша: Обмен местами соседних символов.
- Шаблон:Клавиша: Очищает содержание строки до курсора и копирует его в буфер обмена экземпляра bash (kill ring).
- Шаблон:Клавиша: Убирает слово до курсора и копирует его в буфер обмена экземпляра bash (kill ring).
- Шаблон:Клавиша: добавляет содержимое буфера обмена экземпляра bash от позиции курсора.
- Шаблон:Клавиша: Посылает сигнал SIGTSTP текущему заданию, который приостанавливает его выполнение, и возобновляет приём команд. Для возобновления его выполнения в фоновом режиме можно ввести команду bg. Для того, чтобы вернуть его из фонового режима или приостановки, можно выполнить fg.
- Шаблон:Клавиша: Прервать текущий процесс с дампом памяти (core dump), послав ему сигнал.
- Шаблон:Клавиша (Шаблон:Клавиша): Откат редактирования.
- Шаблон:Клавиша: переход к последней команде в истории.
- Шаблон:Клавиша: (назад) перемещает курсор назад на одно слово (эквивалентно клавише Шаблон:Клавиша).
- Шаблон:Клавиша: Сокращает слово после курсора.
- Шаблон:Клавиша: (вперёд) перемещает курсор вперёд на одно слово (эквивалентно клавише Шаблон:Клавиша).
- Шаблон:Клавиша: Меняет два слова перед курсором местами.
- Шаблон:Клавиша: Добавляет последний аргумент предыдущей команды.
- Шаблон:Клавиша: Заменить букву под курсором на заглавную.
- Шаблон:Клавиша: Заменить букву под курсором на строчную.
- Шаблон:Клавиша Шаблон:Клавиша: Переносит курсор к началу строки от текущей позиции, и обратно.
- Шаблон:Клавиша Шаблон:Клавиша: Редактирует текущую строку в программе $EDITOR, или vi, если не предопределено.
- Шаблон:Клавиша Шаблон:Клавиша: Вывод на экран информации о версии текущего экземпляра bash.
Стартовые скрипты
Bash при запуске вызывает команды из множества различных скриптов.
Когда bash вызывается как интерактивная оболочка входа в систему, первым делом он читает и вызывает команды из файла /etc/profile, если этот файл существует. После чтения этого файла он смотрит следующие файлы в следующем порядке: ~/.bash_profile, ~/.bash_login и ~/.profile, читает и вызывает команды из первого, который существует и доступен для чтения. При выходе bash читает и выполняет команды из файла ~/.bash_logout.
Когда запускается интерактивная оболочка, но не для входа в систему, bash читает и исполняет команды из файлов /etc/bash.bashrc и ~/.bashrc, если они существуют. Это может быть отменено опцией -norc. Опция -rcfile file заставит bash использовать команды из файла file вместо /etc/bash.bashrc и ~/.bashrc.
Переносимость
Первой строкой скрипта должна быть запись #!/bin/bash, если известен абсолютный путь к исполняемому файлу, либо #!/usr/bin/env bash для автоматического определения пути к исполняемому файлу через команду env с помощью переменной окружения PATH<ref>Шаблон:Cite web</ref>.
Скрипты оболочек, написанные со специфическими для bash особенностями, не будут работать на системах, где используется Bourne shell (sh) или один из его аналогов, если bash не был установлен в качестве дополнительной оболочки. Эта проблема стала особенно важной, когда Ubuntu начал с октября 2006 года поставлять Debian Almquist shell (dash) как скриптовую оболочку по умолчанию, что привело к неработоспособности многочисленных скриптов.
Графический интерфейс к скриптам
Существует множество программ, позволяющих создавать графический интерфейс к bash-скриптам.
- dialog — утилита, позволяющая создавать диалоговые окна в консоли, использует библиотеки curses и ncurses.
- whiptail — аналог утилиты dialog, использует библиотеку newt.
- zenity — наиболее популярное приложение для создания графического интерфейса к скриптам.
- kdialog — аналог zenity в среде KDE.
- yad — форк zenity, с большими возможностями.
- xdialog — замена dialog, предназначенная для придания запускаемым из терминала программам интерфейса X Window System.
- gtkdialog — наиболее функциональная утилита для создания графических приложений на bash-скриптах.
См. также
Примечания
Ссылки
- Шаблон:Cite web
- Шаблон:Cite web
- Шаблон:Cite web
- Шаблон:Cite web
- Шаблон:Cite web
- Шаблон:Cite web
- Шаблон:Cite web
- Шаблон:Cite web
Шаблон:Команды Unix Шаблон:Интерфейсы пользователя в Unix-подобных системах Шаблон:GNU Шаблон:POSIX.1-2008
- Страницы с ошибками скриптов
- Википедия:Страницы с шаблоном Другие значения с устаревшим параметром
- Программное обеспечение по алфавиту
- Командные оболочки Unix
- Программное обеспечение проекта GNU
- Скриптовые языки
- Свободное программное обеспечение, написанное на Си
- Свободное кроссплатформенное программное обеспечение