Поиск по сайту:

Скрипты Bash — функции, объясненные примерами

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

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

Как определить функции в Bash

При работе с функциями необходимо понимать две важные вещи.

  • Определение функции,
  • Вызов функции.

Подобно тому, как вы создаете сценарии bash и запускаете их, вам следует определить функцию и вызвать ее для запуска функции.

Существует два синтаксических способа определения функции в bash. Первый способ — использовать встроенное ключевое слово bash "function", за которым следует имя функции. Блок кода будет записан внутри фигурных скобок {}.

function [name] {
Block of code
}

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

[name](){
Block of Code
}

Какой выбрать? Ну, это всегда личный выбор. Выбор одного над другим не имеет недостатков.

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

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

[name](){ first_line; second_line; }

Компактные функции

ЛУЧШИЙ ПРАКТИКУ:

  1. Попробуйте выбрать любой синтаксис и будьте последовательны с ним.
  2. Если вы работаете в среде совместной работы, важно, чтобы все придерживались одних и тех же стандартов при кодировании.

Соглашение об именовании

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

Взгляните на приведенный ниже пример. Я создал функцию с именем "hello_world" в змеином стиле, которая просто выводит hello world на стандартный вывод (терминал).

hello_world() {
echo "Running Simple Hello World Function"
}

hello_world

Как вызвать функцию в Bash

Давайте создадим простую функцию очистки с именем "log_cleanup". Целью этой функции является удаление файлов ".log" старше 30 дней.

log_cleanup(){
  echo "Running Cleanup On Older Logs - 30 days"
  find /home/karthick/Documents/Projects/logs/ -name "*.log" -type f -mtime +30 -delete
  echo "Cleanup Completed"
}

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

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

#!/usr/bin/env bash

#### FUNCTION DEFINITION ####

log_cleanup(){
  echo "Running Cleanup On Older Logs - 30 days"
  find /home/karthick/Documents/Projects/logs -name "*.log" -type f -mtime +30 -delete 
  echo "Cleanup Completed"
}

Calling the function

log_cleanup  

Вызов функции в Bash

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

line 3: log_cleanup: command not found

Ошибка «Команда не найдена»

Почему так? Когда вы запустите скрипт, код будет интерпретироваться построчно сверху вниз. Он прочитает функцию и загрузит ее в среду bash (память). Но здесь вы вызываете функцию еще до того, как интерпретатор прочитает и загрузит функцию в свое окружение.

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

Вызов функции из другой функции

Статус выхода и возвращаемое значение

Каждая команда Linux возвращает статус выхода (0-255). Ноль считается успешным, а остальные коды выхода считаются неудачными с разными значениями. Аналогично, когда вы запускаете функцию, она также возвращает статус завершения последней команды запуска в функции.

Позвольте мне еще раз запустить ту же функцию «очистки». Но я указал путь, который недоступен на моем компьютере, поэтому команда find завершится неудачей. Я использую $?, чтобы получить статус завершения внутри скрипта, как показано на изображении.

Running Cleanup On Older Logs - 30 days
find: '/home/karthick/Documents/Projectss/logs': No such file or directory
Cleanup Completed
Exit status of function log_cleanup is ⇒ 0

Статус выхода и возвращаемое значение

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

Чтобы преодолеть это поведение, вы можете использовать встроенный в bash оператор "return".

type -a return
return is a shell builtin

Return принимает целое число [N] и выходит из функции и передает возвращаемое значение вызывающей стороне (функции). Прежде чем использовать оператор return, вы должны понять несколько правил его использования. Как говорилось ранее, return принимает целочисленные значения от 0 до 255. Оператор return будет использовать статус завершения последней запущенной команды, если не передан аргумент (целое значение) или значение превышает 255.

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

#!/usr/bin/env bash

#### FUNCTION DEFINITION ####

log_cleanup(){
 echo "[ INFO ] - Running Cleanup On Older Logs - 30 days"
 if [[ -d "/home/karthick/Documents/Projectss/logs" ]]
 then
   find -name "*.log" -type f -mtime +30 -delete
   echo "[ SUCCESS] - Cleanup Completed"
 else
   echo "[ ERROR ] - Directory path wrong... Cleanup has not happened..."
   return 1
 fi
}

Calling the function

log_cleanup 
echo "++ Exit status of log_cleanup function is ==> $?"

Посмотрите на результат ниже. Функция возвращает код выхода 1 из оператора return.

[ INFO ] - Running Cleanup On Older Logs - 30 days
[ ERROR ] - Directory path wrong… Cleanup has not happened…
++ Exit status of log_cleanup function is ==> 1

Код состояния выхода

Передача аргументов в функцию

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

cat > arg_test.sh
#!/bin/bash
echo "Value passed in \$1 is = $1"

howdy(){
   echo "value of \$1 inside function is = $1"
}

howdy # Function Call

Скопируйте и запустите приведенный выше фрагмент, чтобы увидеть разницу. Строка "Howdy" передается в качестве первого аргумента скрипта.

./arg_test.sh howdy
Value passed in $1 is = howdy
value of $1 inside function is =

Из вывода вы можете видеть, что $1 внутри функции печатает пустое значение, поскольку $1 внутри функции отличается от $1 вне функции. они имеют одно и то же имя.

Чтобы передать аргументы функции, после имени функции оставьте пробел и передайте аргументы, как показано на рисунке ниже. Каждому аргументу, разделенному пробелом, будет присвоена соответствующая переменная $1$N, и вы можете использовать эту переменную внутри функции для обработки аргументов.

log_cleanup $1 $2 ….. $N

Передача аргументов в функцию

Как вы видите на скриншоте выше, я передаю в качестве аргументов имя каталога и количество дней.

Область действия переменной для функции

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

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

 #!/bin/bash

outside_function="This variable is from outside the function"

func1(){
  inside_function="This variable is from inside the func1"
}

func1
echo $outside_function
echo $inside_function

Глобальная переменная

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

Чтобы сделать переменные локальными для функции, вы можете использовать встроенное ключевое слово bash "local". Ключевое слово local ограничивает область действия переменной от глобальной до локальной, и доступ к переменной можно получить только во время выполнения функции.

#!/bin/bash

outside_function="This variable is from outside the function"

func1(){
  local inside_function="This variable is from inside the func1"
}

func1
echo $outside_function
echo $inside_function

Локальная переменная с использованием ключевого слова local

Рекомендуем прочитать:

  • Скрипты Bash: переменные, поясняемые примерами

Когда используется ключевое слово local, вы можете использовать одни и те же имена переменных в разных функциях.

Идентичные имена переменных

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

Модульность и ремонтопригодность

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

Давайте возьмем пример. Вы создали 20 сценариев, и в каждый сценарий вы включили функцию log_cleanup, которую мы видели в предыдущих разделах для выполнения служебных задач. Вместо включения этой функции во все 20 сценариев вы можете создать одно определение функции и импортировать его в 20 сценариев. Таким образом, вы достигаете модульности, а также функций многократного использования. Это похоже на операторы import в Python, операторы include в C и т. д.

Посмотрите на изображение ниже. Я создал два сценария с именами script1.sh и script2.sh, а функция log_cleanup записана в отдельный файл с именем cleanup. ш.

Функция очистки

Я импортирую функцию, выполнив команду source. Команда source запустит файл, переданный в качестве аргумента, и загрузит переменную или функции в текущий сеанс bash. Таким образом, когда вы запускаете log_cleanup из другого файла сценария, функция уже загружена в текущую среду и доступна.

Функции поиска

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

Внимание! Вы также можете запускать сценарии оболочки с помощью команды source, которая запустит сценарий в текущей оболочке вместо создания подоболочки для запуска сценария.

Заключение

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

Связанное чтение:

  • Скрипты Bash: цикл For с примерами
  • Скрипты Bash: циклы while и Until с примерами
  • Разница между определением переменных Bash с экспортом и без него
  • Команда Bash Echo с примерами для Linux
  • Руководство по Bash Heredoc для начинающих
  • Перенаправление Bash, объясненное примерами

Статьи по данной тематике