MyTetra Share
Делитесь знаниями!
Особенности синтаксиса вызова функций в Bash-скриптах
Время создания: 17.05.2022 17:47
Автор: xintrea
Текстовые метки: linux, bash, функция, вызов, синтаксис, присвоение, переменная
Раздел: Компьютер - Linux - Bash - Программирование на Bash
Запись: xintrea/mytetra_syncro/master/base/1652798878xuy3ix1sim/text.html на raw.github.com

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


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


Для начала вот что нужно знать о функциях.


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


Функция без входных параметров в Bash оформляется так:



# Получение IP-адреса текущего хоста

getCurrentHostIp ()

{

myIp=$(

LANG=C /sbin/ifconfig |

sed -ne $'/127.0.0.1/ ! {

s/^[ \t]*inet[ \t]\\{1,99\\}\\(addr:\\)\\{0,1\\}\\([0-9.]*\\)[ \t\/].*$/\\2/p;

}')


# Если найдено несколько основных IP-адресов, выбирается только первый

myIp=`echo -n ${myIp} | cut -d ' ' -f 1`


echo -n ${myIp}

}



Функция с входными параметра выглядит так:



# Запуск SQL-команд из указанного файла

# Параметр 1 - имя файла с SQL-командами

# Параметр 2 - имя базы данных (опционально)

runPsqlCommandsFromFile () {


local fileName=$1

local dbName=$2


if [ ! -f $fileName ]; then

echo "Не найден файл с SQL-командами $fileName"

exit 1

fi


local dbNameLen=`echo -n "$dbName" | wc -c`


# Если имя БД не задано

if [[ dbNameLen -eq 0 ]]; then

sudo -u postgres bash -c "cd /tmp ; psql -f $fileName"

else

sudo -u postgres bash -c "cd /tmp ; psql -d $dbName -f $fileName"

fi


}



И вот какие особенности есть у вызовов функции.


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


Например, вышеприведенная функция getCurrentHostIp() возвращает IP-адрес в виде строки. Вызов функции, которая что-то возвращает необходимо делать так, чтобы ее возвращаемое значение обязательно помещалось в переменную или обрабатывалось каким-нибудь условием. А сам вызов функции должен оборачиваться конструкцией $(...):



ip=$(getCurrentHostIp)



Если функция ничего не возвращает, то ее вызов надо делать без конструкции $(...):



runPsqlCommandsFromFile /tmp/sql.txt



Почему так, почему нельзя написать:



$(runPsqlCommandsFromFile /tmp/sql.txt) # <----- Так нельзя!



Ведь хочется, чтобы все вызова функций выглядели единообразно!


А нельзя писать вызов функции, которая ничего не возвращает через $(...) по следующей причине. Эта конструкция, про сути, вызывает выполнение функции, и все, что функция возвращает, просто "разворачивает" на месте вызова функции.


  • В случае вызова с присвоением, это "разворачивание, вызванное $(...)" попадает в переменную.
  • В случае вызова без присвоения, это "разворачивание, вызванное $(...)" развернется прямо на месте вызова. А это значит, что Bash будет пытаться интерпретировать то, что вернула вызываемая функция.


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


Но проблема в том, что невозможно гарантировать, что внутри функции нет никакого вывода. Если посмотреть на функцию runPsqlCommandsFromFile() то кажется, что она ничего не пишет в стандартный поток (за исключением обработки случая отсутсвия файла, при котором эта функция завершит весь скрипт). Но это только кажется. На деле, вызываемые команды могут генерировать какой-то консольный вывод. Например, команда psql будет выдавать в консоль некоторые подробности своей работы.


Теперь вопрос: что произойдет, если Bash попытается интерпретировать эти совершенно не предначеные для интерпретации Bash строки? Да, будет ошибка.


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


Так же в этом разделе:
 
MyTetra Share v.0.59
Яндекс индекс цитирования