BASH
Bash (Bourne Again SHell) - это командный интерпретатор и язык управления в UNIX-подобных операционных системах. Он предоставляет интерфейс командной строки для взаимодействия с операционной системой с помощью текстовых команд. При использовании той или иной команды происходит запуск связанной с ней программы.
Каждой системе Unix для правильной работы необходимо наличие оболочки Bourne shell. Система Linux использует улучшенную версию оболочки Bourne shell — bash,
или «заново рожденную» оболочку. Оболочка bash является оболочкой по умолчанию в большинстве дистрибутивов Linux, а путь /bin/sh, как правило, — ссылка на эту
оболочку. Многие важные части системы в действительности являются сценариями оболочки — текстовыми файлами, которые содержат последовательность команд оболочки.
Основы синтаксиса BASH
Символы * ? []. Сопоставление с шаблоном или wildcarding.
-
В Linux символ звездочки * соответствует любой последовательности (за исключением начальной точки в именах файлов или каталогов) из любого числа символов в путях к файлам или каталогам
-
Знак вопроса ?, соответствует любому единичному символу, за исключением начальной точки в именах файлов или каталогов.
-
Так же для шаблонизации запросов можно использовать квадратные скобки [ ], для запроса у командной оболочки соответствия одному из символов набора. Если первый символ в скобках — восклицательный знак (!) или «шляпка» (^), то шаблон определяет все что угодно, кроме оставшихся символов в скобках.
Сопоставление с шаблоном происходит на уровне оболочки (shell). При вводе команды или условия с использованием метасимволов для сопоставления, оболочка выполняет соответствующие шаги. Давайте рассмотрим основные шаги, которые происходят "под капотом":
- Развертывание метасимволов: Когда оболочка видит метасимволы, такие как *, ?, или [], она развертывает их в соответствующие наборы файлов или символов.
- Подстановка значений: Получив развернутый результат, оболочка подставляет эти значения в команду или условие.
- Выполнение команды или проверка условия: Затем оболочка выполняет команду или проверяет условие с учетом развернутых значений.
Сопоставление с образцом работает только с именами файлов и каталогов. Оно неприменимо для имен пользователей, хостов и других типов аргументов.
# Найти все файлы с расширением .txt в текущей директории
ls *.txt
# Найти файлы, состоящие из одного символа в текущей директории
ls ?
# Найти файлы с расширением .jpg или .png в текущей директории
ls *.[jp][np]g
Кавычки ' ' " "
Одинарные кавычки ''
Все символы между двумя одинарными кавычками, включая пробелы, составляют один параметр.
Двойные кавычки ""
Двойные кавычки (") работают так же, как одинарные, за исключением того, что оболочка расширяет любые переменные, которые в них появляются.
Переменные в bash
Синтаксис переменных
Переменные в языке bash должны начинаться с символа латинской буквы или символа подчеркивания _, далее могут следовать алфавитно-цифровые символы.
Новую переменную не обязательно декларировать, досточно написать ее имя и присвоить ей значение.
# Создаем переменную, присваивая ей значение
MY_VAR="some text"
# Выводим значение переменной
echo $MY_VAR
Вычисление переменных
Командная оболочка может определять переменные и сохранять в них значения. Переменная оболочки очень похожа на переменную в алгебре — у нее есть имя и значение. Когда командная оболочка вычисляет переменную, она заменяет имя переменной на ее значение.
# Следующая команда вычислит переменную HOME и выведет ее сзачение в консоль
printenv HOME
# Следующая команда вычислит переменную, добавит ее в путь и передаст на выполнение команде
cd $HOME/Downloads
Логические операторы && ||
&& - логический оператор "и", который выполняет следующую команду только в том случае, если предыдущая команда (условие) успешно завершена (возвращает код завершения 0).
С помощью этого оператора можно компактно выполнять оператор if
|| - логический оператор "или", который выполняет следующую команду только в том случае, если предыдущая команда завершилась неуспешно (возвращает код завершения, отличный от 0).
Можно применять вместо условия else
cd ~/tmp && touch test_file.txt || echo "No such dir"
# если команда cd перейдет в указанный каталог - в нем будет создан указанный файл,
# а если такого католога нет будет выведено сообщение об ошибке
Проверка условий [test]
Поскольку команда test так широко используется в сценариях, она встроена во многие версии оболочки shell. Она ускоряет выполнение сценариев, поскольку оболочке не нужно запускать отдельную команду для каждого теста.
Существуют десятки операций проверки условий, которые делятся на три основные категории: операции проверки файлов, проверки строк и арифметической проверки. Руководство info содержит полную онлайн-документацию, а его страница test(1) может использоваться для получения быстрой справки.
Проверка файлов
Большинство проверок файлов, например -f, называются унарными операциями, потому что для них требуется только один аргумент — файл для проверки.
Операторы проверки типов файлов:
- -e — возвращает значение true, если файл существует;
- -s — возвращает значение true, если файл не пустой.
- -f — Обычный файл
- -d — Каталог
- -h — Символическая ссылка
- -b — Блочное устройство
- -c — Символьное устройство
- -p — Именованный канал
- -S — Сокет
Операторы проверки прав доступа:
- -r — На чтение
- -w — На запись
- -x — На исполнение
- -u — На Setuid
- -g — На Setgid
- -k — На Sticky
Операторы сравнения файлов:
- -nt — (newer than — младше) [ file1 -nt file2 ] Возвращает true, если файл file1 имеет более новую дату изменения, чем файл file2
- -ot — (older than — старше) делает обратное.
- -ef — сравнивает два файла и возвращает true, если они имеют общие номера дескрипторов inode и устройства
Проверка строк:
- = — который возвращает значение true, если его операнды равны
- != — который возвращает значение true, если его операнды не равны
- -z — возвращает значение true, если аргумент пуст ([ -z "" ] возвращает 0);
- -n — возвращает значение true, если его аргумент не пуст ([ -n "" ] возвращает 1).
Арифметическая проверка:
- -eq — Возвращает значение true, если первый аргумент равен второму
- -ne — Возвращает значение true, если первый аргумент не равен второму
- -lt — Возвращает значение true, если первый аргумент меньше, чем второй
- -gt — Возвращает значение true, если первый аргумент больше, чем второй
- -le — Возвращает значение true, если первый аргумент меньше или равен второму
- -ge — Возвращает значение true, если первый аргумент больше или равен второму
[[ test_exp ]] - для тестирования различных условий удобно использовать двойные прямоугольные скобки. Команда help test дает ценные подсказки о выражениях проверки условий. Для сравнения строковых значений мы используем двойные квадратные скобки, в том числе потому что они поддерживают регулярные выражения.
DIR="/path/to/some/dir"
# Если указанная директория существует будет выведено ее содержание
[[ -d $DIR ]] && ls "$DIR"
# Если диретории нет - выдаст ошибку и завершит сценарий
[[ -d $DIR ]] || { echo "error: no such directory: $DIR" ; exit ; }
Фигурные скобки { }
{ COMMANDS ; } - в фигурных скобках удобно указывать группу команд, которые необходимо выполнить
[[ -n "$DIR" ]] && [[ -d "$DIR" ]] && cd "$DIR" || { echo "No such dir"; exit 4; }
# В данном примере происодит проверка нескольких условий
# Является ли переменная DIR не пустой и является ли каталогом, если так то оболочка перейдет в этот католог
# В противном случае будет напечатано сообщение и произведен выход из оболочки кодом ответа 4
# Код ответа хранится в переменной $?
Двойные круглые скобки (( ))
Двойные круглые скобки (( ... )) используются для выполнения арифметических вычислений с целыми числами. Внутри таких скобок можно использовать арифметические операторы и переменные. Знак $ не требуется использовать для ссылки на переменные внутри двойных круглых скобок.
# Арифметическое выражение:
(( result = 3 * (2 + 4) ))
# Сравнение:
(( x > y ))
# Инкремент и декремент:
(( counter++ ))
(( counter-- ))
# Цикл for
for (( i=0; i<size; i++ ))
Важно понимать, что двойные круглые скобки без знака $ интерпретируются как выполнение одной или нескольких команд. Они не возвращают результат вычислений, который можно было бы присвоить переменной. Выражения в (( )) возвращают не результат вычислений, а только статус (код возврата), который можно проверить в переменной $?, устанавливаемой после выполнения каждого оператора.
# знак $ можно использовать для арифметических вычислений в операции присваивания значения переменной, например:
a=$(( (a + b) * 3 - c ))
# В операции присваивания значения переменной пробелы вокруг знака равенства не допускаются.
# Синтаксически вся инструкция присваивания должна быть одним «словом».
# Однако внутри круглых скобок допускается использовать пробелы, потому что круглые скобки определяют границы этого «слова».
declare -i MYVAR. Затем с ней можно выполнять арифметические операции и присваивать ей значение без использования двойных круглых скобок или знака $.
Логические операции
Bash позволяет проводить сравнение как чисел, так и строк.
a=2;b=4
[“$a” -eq “$b”] #числа равны
[“$a” -ne “$b”] #числа не равны
[“$a” -gt “$b”] #число а больше b
[“$a” -ge “$b”] #число a больше или равно b
[“$a” -lt “$b”] #число a меньше b
[“$a” -le “$b”] #число a меньше или равно b
a=Hello;b=hello
[“$a” = “$b”] #строки равны
[“$a” == “$b”] #строки равны
[“$a” != “$b”] #строкине равны
[“$a” \> “$b”] #строка а больше b
[“$a” \< “$b”] #строка a меньше b
[ -n “$a”] #строка a не пустая
[ -z “$a”] #строка a пустая
Here-документы EOF
Here-документы, также известные как "here-strings" или "heredocs", являются специальным типом конструкции в языках программирования и скриптовых языках, который позволяет встраивать блок текста или команд в исходный код без необходимости явного использования кавычек или других разделителей.
cat << EOF
Это пример here-документа.
Многострочный текст здесь.
Можно вставлять переменные, например: $VARIABLE.
EOF
В этом примере << EOF указывает на начало here-документа, а EOF (End Of File) указывает на его завершение. Все, что находится между ними, будет обработано интерпретатором Bash как текстовый блок. Here-документы часто используются для передачи многострочных данных в утилиты или скрипты, например, для создания файлов конфигурации или для форматированного вывода.
Маркер (EOF) может быть любой строкой, но не забывайте использовать один и тот же маркер в начале и в конце here-документа. Кроме того, соглашение требует, чтобы маркер состоял только из прописных букв.
Here-документы могут быть очень удобными, когда вам нужно вставить большой блок текста в ваш код, не заботясь о специальных символах экранирования или форматировании строки. Они также поддерживают подстановку переменных, что делает их еще более гибкими.
Массивы в BASH
В информатике и программировании массивы — это переменные, содержащие несколько элементов, на которые можно ссылаться с использованием целочисленных индексов. Иначе говоря, массив — это переменная, содержащая список, а не скаляр или одиночное значение.
Ассоциативный массив — это список, элементы которого индексируются строками, а не целыми числами. То есть это список пар «ключ – значение», образующий словарь или таблицу поиска, в котором ключи хешируются для формирования области памяти.
bash поддерживает переменные, способные хранить одномерные массивы с целочисленными индексами и ассоциативные массивы переменных
Списки по своей природе — упорядоченные коллекции, тогда как хеши — нет, а такие операции, как сдвиг (shift) или добавление в конец (push), имеют смысл только для упорядоченных наборов элементов. С другой стороны, вам никогда не понадобится сортировать ключи в списке, но эта операция имеет определенный смысл для хешей.
Списки
Массивы, также известные как списки, — это переменные, содержащие несколько элементов, которые индексируются целыми числами.
В bash индексация начинается с нуля, а массивы могут объявляться как с помощью командdeclare -a, local -a, readonly -a, так и простым присваиванием, например: mylist[0]=foo или mylist=() (пустой список). После объявления переменной списком простое присваивание, такое как mylist+=(bar), будет действовать как операция добавления элемента в конец списка.
echo ${!array_int[@]} #Получить индексы массива
echo ${#array_int[@]} #Получить размер массива
array_int[0]=0 #Перезаписать значение первого элемента
array_int+=(6) #Добавить в конец массива элемент со значением
array_out=() #Создать пустой массив
array_out=$(ls) #Записать вывод ls как строку
array_out=($(ls)) #Записать вывод ls как набор строковых элементов
Хеши словари
Ассоциативные массивы, также известные как хеши или словари, — это списки, индексами в которых являются строки, а не целые числа. Кроме прочего, такие массивы очень удобны для подсчета или «уникализации» (то есть игнорирования или удаления дубликатов) строк.
В отличие от списков, хеши обязательно должны объявляться с помощью команд declare -A, local -A или readonly -A, а при обращении всегда необходимо указывать индекс.
Дескрипторы stdin, stdout, stderr
При запуске любого процесса ОС ему выделяется минимум 3 файловых дескриптора. Дескрипторам присвоены следующие названия: стандартный ввод (stdin), стандартный вывод (stdout) и стандартная ошибка (stderr).
-
stdin - ресурс ввода в программу (по умолчанию - клавиатура).
-
stdout - место, куда по умолчанию выполняемая программа пишет результат своих действий (консоль, файл).
-
stderr - дескриптор, в который пишутся сообщения об ошибках. Зачастую перенаправляются в консоль.
Перенаправление потоков ввода и вывода
Оболочка управляет вводом и выводом запускаемых команд. В языке Bash существует возможность перенаправления ввода-вывода - это механизм, который позволяет изменять источники данных (ввода) и места, куда направляется результат выполнения команды (вывода). Это позволяет управлять потоками данных в командной строке, не внося изменения в код программы.
Основные формы перенаправления ввода-вывода:
echo "Test string1" > output.txt # Создает или перезаписывает файл содержанием команды echo
echo "Test string2" >> output.txt # Добавляет в файл текстовую строку
grep "something" < file.txt # Передаем на stdin команды grep файл
# Команды перенаправляние можно использовать одновременно
grep "something" < file.txt > grep_output.txt
# Если команда завершиться с ошибкой, сообщение об ошибке будет записано в файл
cp nonexistent.txt file.txt 2> errors.txt # перезапишет файл
cp nonexistent.txt file.txt 2>> errors.txt # добавит строку в файл
# Перенаправляем все сообщения в файл
cat goodfile.txt nonexistent.txt &> all.output
Подробно о потоках ввода-вывода
Конвеер pipe |
Символ |, который называется "вертикальной чертой" или "пайпом" (pipe), используется в Linux для передачи данных между программами через стандартные потоки ввода и вывода. Это позволяет создавать цепочки команд, где вывод одной команды становится вводом для другой команды.
Примеры использования pipe |:
command1 | command2 # В этом случае, вывод command1 становится вводом для command2. Это позволяет цепочке команд обрабатывать данные последовательно.
ls -l | grep ".txt" # Здесь команда ls -l выводит список файлов и передает его ввод в команду grep, которая ищет строки, содержащие ".txt".
history | awk '{print $2}' | sort | uniq -c | sort -nr | head -n 10
# Эта цепочка команд анализирует историю команд в терминале. Она использует awk для извлечения второго столбца (команд),
# затем сортирует, подсчитывает уникальные команды, снова сортирует по количеству использований и выводит 10 наиболее часто используемых команд.
Нотация >&
Нотация >& используется для перенаправления потоков вывода (stdout и stderr)
Примеры использования нотации >&:
command > output.txt 2>&1
# В этом примере stdout команды command будет перенаправлен в файл output.txt,
# а stderr будет также перенаправлен в тот же файл (2>&1).
command 2>&1
# Эта команда перенаправляет stderr в тот же поток, что и stdout, что означает, что оба потока будут выводиться вместе.
command1 &> /dev/null
# Нотация 2>&1 может быть сокращена нотацией &>
Код возврата: $?
Переменная $? содержит код возврата последней команды, выполненной оболочкой.
$? представляет собой специальную переменную, которая хранит код завершения последней выполненной команды. Код завершения (или код возврата) - это число, которое указывает на результат выполнения команды. Обычно код 0 означает успешное выполнение, а другие значения могут указывать на различные ошибки или условия.
Важно понимать, что некоторые программы, например diff и grep, используют ненулевые коды возврата для обозначения нормальных условий. Например, grep возвращает 0, если находит что-то, соответствующее шаблону поиска, и 1, если это не так. Для этих программ код возврата 1 — не ошибка, поэтому grep и diff применяют код возврата 2, если сталкиваются с реальной проблемой. Если вы считаете, что программа может использовать ненулевой код возврата для указания успешного действия, изучите ее справочную страницу. Коды возврата обычно рассматриваются в разделе EXIT VALUE или DIAGNOSTICS.
$? следует проверять сразу после выполнения команды, так как при каждом выполнении новой команды значение $? обновляется. Использование $? полезно для проверки успешности выполнения предыдущей команды в скриптах и принятия решений на основе этого результата.
Испорические расширения в bash: !$, !!, !*
!$ представляет собой историческое расширение, используемое для получения последнего аргумента из предыдущей команды в истории. Это позволяет повторно использовать последний аргумент без необходимости повторного набора. Это может быть полезным в различных сценариях, например, при выполнении нескольких операций с одним и тем же аргументом.
!$ относится только к последнему аргументу; если вам нужен доступ к другим аргументам предыдущей команды, можно использовать другие исторические расширения, такие как:
- !! - полная предыдущая команда
- !n - где n - номер команды в истории.
- !* - раширение истории, которое соответствует всем введенным в предыдущей команде аргументам
# Например, с помощью команды ls проверяем файлы по шаблону
ls *.txt
# С помощью команды rm удаляем файлы по тому же шаблону
rm !$
Аргументы команды
Индивидуальные аргументы: $1, $2
$1, $2 и все переменные, названные положительными ненулевыми целыми числами, содержат значения параметров скрипта, или аргументов.
# При вызове скрипта в него можно передать определенные значения переменных,
# которые потом можно обрабатывать в теле скрипта
./my-script var1 var2 var3
# Переменной $1 будет присвоено значение var1, $2=var2, $3=var3
Количество аргументов $#
Переменная $# содержит количество аргументов, переданных сценарию. Когда значение $# равно 0, аргументов не остается, поэтому у $1 значения нет.
Все аргументы $@
Переменная $@ представляет все аргументы скрипта и очень полезна для передачи их команде внутри скрипта.
Имя сценария: $0
Переменная $0 содержит имя сценария и полезна для генерации диагностических сообщений.
ID процесса: $$
Переменная $$ содержит идентификатор процесса оболочки PID.
Фоновый режим &
В языке bash существует возможность запусктить любую команду или скрипт в фоновом режиме. Для этого достаточно в конце строки с командой поставить оператор &
ping google.ru &> google_ping.log &
# Данная команда будет пинговать google и писать весь вывод в текстовый файл
# При этом вернет управление в оболочку bash
Список фоновых задач jobs
Задачи выполняемые в фоновом режиме не видны администатору, но можно увидеть их список с помощью команды jobs
jobs
[1]- Запущен ping google.ru &> googleru_ping.log &
[2]+ Запущен ping google.com &> googlecom_ping.log &
# Чтобы вывести задачу из фона можна использовать команду fg №-процесса
fg 2
ping google.com &> googlecom_ping.log
# для приостановки задачи используется комбинация Ctrl+Z
# для возобновления остановленной задачи - bg №-задачи
bg 2
# Для остановки задачи - комбинация Ctrl+C
Скрипт на языке BASH
#!/usr/bin/env bash - это шебанг (shebang) или "магическая строка", которая указывает на то, какую интерпретаторскую программу следует использовать для выполнения
скрипта. Данная строка помещается в начало файла-скрипта, она означает, что скрипт должен быть выполнен с использованием интерпретатора Bash, который будет найден в
переменной окружения PATH. Это обеспечивает более переносимый и гибкий способ указания интерпретатора, так как не требуется жесткое указание пути к интерпретатору.
Так же необходимо установить бит исполняемого файла для файла сценария оболочки, а также бит чтения, чтобы оболочка могла прочитать файл. Самый простой способ сделать
это — ввести следующую команду: chmod +rx scriptfile_name
Подробно про скриптинг на bash