Bash-скрипт - это сценарий командной строки, написанный для оболочки bash или подобных ей. Как правило, применяются при работе в терминале Linux-систем, хотя их можно использовать и в Windows. Представляет из себя набор команд, объединенных в файл по какому-то общему признаку. Результат их выполнения является либо ценным сам по себе, либо служит для выполнения других сценариев. Bash-функции позволяют автоматизировать большинство часто выполняемых процессов.

Bash-функции для Терминала

Оболочка Терминала позволяет выполнить несколько команд за раз. Их нужно просто записать через точку с запятой в одну строку. Например вот так:

pwd ; whoami

Написанный скрипт выводит данные о текущем каталоге и имени пользователя или группы пользователей. Этот набор команд, записанных в одну строку уже можно считать полноценным bash-скриптом. Благодаря этому подходу можно работать сразу с несколькими командами, которые можно совмещать в одной строке. Максимальное число возможных аргументов узнается с помощью команды: getconf ARG_MAX.

Однако его функциональность выходит далеко за пределы написания нескольких простых команд в одну строку. Никто не мешает вам записать их сразу в файл и при необходимости вызывать данный файл для выполнения. Примечательно, что в этот файл можно записать более сложные конструкции: регулярные выражения, функции, параметры для обработки и так далее.

Файл для Bash-скриптов

Создайте пустой файл, дав ему имя на латинице. Откройте его для редактирования в любом удобном редакторе кода. В первой строке файла нужно прописать команду для его инициализации: #!/bin/bash. Таким образом вы дадите понять терминалу, что это bash-скрипт для исполнения.

После инициализации можно писать уже сами команды. В отличии от командной строки здесь рекомендуется разделять их не знаком точки с запятой, а переносом на другую строку:

pwd
whoami

Это нужно для более удобного чтения содержимого. Однако, если вам удобнее записывать команды в одну строку, то это тоже можно делать в файле скрипта. Только не забывайте разделять их точкой с запятой. Вне зависимости от вида записи оболочка их обработает.

Отдельно стоит выделить возможность комментирования содержимого файла. Комментарии не обрабатываются терминалом и нужны лишь для того, чтобы было удобнее редактировать код в самом файле. Чтобы написать свое примечание к отрезку кода используйте символ # с новой строки:

# Вывод данных о директории
pwd
# Вывод данных о пользователе
whoami

Чтобы файл со скриптом корректно обрабатывался оболочкой его требуется инициализировать, сделав его исполняемым. В противном случае вы получите сообщение об ошибки при попытке запустить написанный скрипт в терминале.

Ошибка инициализации

Ошибка инициализации

Выполнить инициализацию файла можно с помощью специальной команды через тот же терминал:

chmod +x ./file_name

После выполнения данной команды не будет никаких сообщений. Проверить корректность ее выполнения можно только вызвав файл со скриптом. Если ошибки инициализации не последует, то значит, что разрешения были присвоены корректно.

Работа с переменными

Одно из главных преимуществ файлов с Bash-сценариями - возможность хранить в них результат работы других команд или скриптов для последующего использования. Это же в свою очередь позволяет использовать продвинутый функционал, в том числе и Bash-функции, где в качестве аргумента будут передаваться результаты работы предыдущих скриптов.

Переменные условно можно разделить на две группы:

  • переменные среды - они уже записаны в самой среде;
  • пользовательские переменные - переменные, созданные пользователем.

Далее рассмотрим процесс работы с каждым из данных типов переменных подробнее.

Переменные среды и пользователя

Они уже зарезервированы средой разработки и отвечают за выполнение того или иного действия. По-сути, это и есть команды. Для инициализации переменной нужно поставить знак $. Примечательно, что его можно ставить даже в кавычках - это не помешает системе его распознать. Вот пример вызова переменной среды:

echo "Home for the current user is: $HOME"

Команда echo выводит на дисплей указанную строку, а переменная $HOME отображает адрес домашней папки активного пользователя. Использовать переменные можно также в теле функций.


Вывод сообщения из Bash-скрипта

Вывод сообщения из Bash-скрипта

Пользовательская переменная отличается от переменной среды только тем, что ее требуется задать заранее. Ее инициализация и использование происходит таким образом:

old=20
person="Adam"
echo "Hi, I’m $person $old years-old"

По итогу выполнения данного скрипта вы должны получить сообщение, в котором человек называет себя и свой возраст.

Примечание. Если вам нужно написать знак доллара, но при этом никакой переменной вызывать вы не собираетесь, то используйте знак слеша перед долларом. Пример:

echo "I have /$1 on my pocket"


Вывод знака $ в сообщении

Вывод знака $ в сообщении

Также на место переменной можно подставлять уже зарезервированные команды терминала. Обычно это делается как раз для того, чтобы записать данные выполнения той или иной команды. Это можно реализовать двумя методами:

  • обернув нужную команду в знаки обратного апострофа: mydir=`pwd`;
  • заключив ее в скобки: mydir=$(pwd).

Гораздо чаще используется второй вариант.

Объявление Bash-функций

Bash-скрипты позволяют писать очень сложные конструкции, в которые включены логические операции, условия, циклы и так далее. Чтобы не повторять по несколько раз один и тот же кусок кода в скрипте, рекомендуется использовать функции. По своим возможностям и методам взаимодействия они не сильно отличаются от аналогов во многих языках программирования.

Функция имеет такой вид: имя_функции(аргумент) { список_команд }. Также допускается написание без аргумента, когда после объявления функции сразу идет перечень команд.

Давайте в качестве примера напишем простую функцию, которая будет выводить сообщение на экран без использования каких-либо сторонних аргументов:

#Тело функции
printstr() {
echo "hello world"
}
#Вывод функции
printstr

Она выводит строку “Привет мир”. Чтобы запустите выполнение функции нужно просто написать ее имя. Использовать какие-то дополнительные знаки для этого не нужно. Функцию можно вызывать сколько угодно по мере выполнения скрипта. Однако обратите внимание на правильность написания названия. Если функция не была ранее объявлена или вы допустили ошибку при ее написании, то в итоге получите сообщение об ошибке, а выполнение скрипта будет прервано.

Также задавая имена для функций обязательно учитывайте, что они должны быть уникальными. Если в скрипте встретиться несколько функций с одинаковыми именами, то более новая просто будет постоянно “затирать” старые. Сообщения об ошибках вы не получите, однако работоспособность скрипта все равно будет нарушена.

Вот пример использования нескольких функций с одинаковыми именами:

function myfunc {
echo "The first function definition"
}
function myfunc {
echo "The second function definition"
}
myfunc
echo "End of the script"

По итогу вы получите сообщение "The second function definition", плюс, сообщение о прекращении скрипта. Хотя, возможно, вы ожидали увидеть текст из первой обозначенной функции.

Вывод значений переменных в сообщении

Вывод значений переменных в сообщении

Команда return

Команда return, прописываемая внутри функции, позволяет вернуть ее целочисленное значение в виде кода завершения. Разберем работу return на примере вот этой функции:

function summation {
#пользователь вводит изначальное целое число
read -p "Enter a value: " value
echo "adding value"
#функция возвращает результат сложения пользовательского числа с 10
return $(( $value + 10 ))
}
summation
#Вывод результата вычислений
echo "The new value is $?"

В указанном примере выполнение любой другой команды до извлечения из переменной $? значения, приведет к потери значения, возвращаемого функцией. Это объясняется тем, что в переменной хранятся только данные последней выполненной команды.


Значение, возвращаемое функцией

Значение, возвращаемое функцией

Также учитывайте другое ограничение команды return - можно использовать только целочисленные значение, плюс, итоговый результат не должен быть больше 255 и меньше -255. Если вам требуется, чтобы функция работала с большими или меньшими значениями, то придется использовать другие подходы.

Запись результатов выполнения функции в переменную

Этот подход позволит обойти ограничения, выставляемые при использовании команды return. Суть заключается в перезаписи данных, выводимых после выполнения функции, в отдельную переменную. Также этот метод будет актуален в том случае, если вам приходится работать не с числами, а строками.

Пример записи результатов выполнения функции в переменную:

function summation {
read -p "Enter a value: " value
echo $(( $value + 10 ))
}
result=$(summation)
echo "The value is $result"

По итогу выполнения данного скрипта конечный итог будет записан в отдельную переменную, а затем выведен на экран пользователя.

Работа с аргументами функций

Функции не только позволяют не копировать по несколько раз большие участки кода, но и использовать дополнительные возможности при работе. Передача функциям аргумента делает их более гибкими, что позволяет быстро поменять один параметр, а не выискивать нужные элементы для изменений по всему телу функции.

Синтаксис написания функции с аргументами выглядит следующим образом: имя_функции аргумент1 аргумент2 ... аргументN. Аргументы отделяются друг от друга пробелом.

Вот пример функции с аргументом:

printstr(){
echo $1
}
#перечень аргументов, передаваемых в echo
printstr "Hello world"

А вот та же самая функция с несколькими аргументами:

printstr(){
echo $1
echo $2
echo $3
}
printstr "Hello world" "Hello world" "Hello world"

Обратите внимание, что обращение к нужному аргументу производится по шаблону: $+номер аргумента. Также стоит заметить, что у вас не получится использовать в теле функции переменную вида $0, так как она зарезервирована под саму bash-функцию.


Вывод аргументов функции

Вывод аргументов функции

Вот еще несколько видов записи аргументов и пояснения к ним:

  • "$*" - раскрытие аргументов происходит в одну строку с разделением через пробел - "$1 $2 $n";
  • "$@" - передает все аргументы, указанные для функции, на новой строке;
  • если нет двойных кавычек, то $* и $@ не отличаются друг от друга.

Передача массива в качества аргумента

Функции можно передать в качестве аргумента сразу массив с данными. Однако, чтобы она корректно приняла все значения из массива, из него потребуется извлечь все данные и сделать их передачу в виде самостоятельных аргументов. Вот примера корректной bash-функции, где в качестве аргумента используется массив с данными:

function func {
local n_array
n_array=("$@")
echo "The new array value is: ${n_array[*]}"
}
#Преобразование массива в обычную переменную
myarray=(1 2 3 4 5)
echo "The original array is ${myarray[*]}"
#Передача переменных массив в функцию
func ${myarray[*]}

В данном примере функция выполнила компиляцию нового массива из переданных ей аргументов.

Локальные и глобальные переменные

Внутри функций можно задавать переменные, однако вести они себя будут немного иначе, чем стандартные переменные в скрипте. Дело в том, что они могут быть скрыты от остального скрипта, то есть исполняться только внутри определенной функции. При попытке вызвать переменную за ее пределами вы столкнетесь с ошибкой.

Переменные внутри bash-скрипта можно условно поделить на глобальные и локальные.

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

Если вам требуется, чтобы объявленная переменная не была доступна за пределами той или иной функции, то сделайте ее локальной. Для этого нужно просто написать ключевое слово local перед ее названием. Пример:

local temp=5

В случае, если пределами функции будет переменная с таким же именем, то это на нее никак не повляет.

Экспорт bash-функций

Вы можете использовать функцию за пределами bash-скрипта, если выполните ее экспорт. Для этого применяется команда declare. Пример ее использования:

printstr(){
echo "hello world"
}
declare -x -f printstr

Синтаксис у команды очень простой: declare -x -f название_функции. Можно использовать в любой части кода, даже внутри функции, если там есть вложенная функция.

Получить доступ к экспортированной функции можно таким образом:

$ source function.sh
$ printstr

Первая команда указывает на скрипт, из которого требуется извлечь функцию. Вторая непосредственно на имя экспортируемой функции.

Экспорт функции

Экспорт функции

Заключение

Bash-скрипты сами по себе позволяют автоматизировать работу с Командной строкой или Терминалом, что полезно для фулл-стак и бэк-энд разработчиков. Благодаря функциям простые скрипты можно превращать в полноценные мини-программы для упрощения администрирования. Также при необходимости разработчик может обращаться к ранее написанным функциям, делая их экспорт в терминал разработчика.