Подобно «настоящим» языкам программирования, Bash тоже имеет функции, хотя и в несколько ограниченном варианте. Функция — это подпрограмма, блок кода который реализует набор операций, своего рода «черный ящик» , предназначенный для выполнения конкретной задачи. Функции могут использоваться везде, где имеются участки повторяющегося кода.
Вторая форма записи ближе к сердцу C-программистам (она же более переносимая).
Как и в языке C, скобка, открывающая тело функции, может помещаться на следующей строке.
Вызов функции осуществляется простым указанием ее имени в тексте сценария.
Пример 22-1. Простая функция
Функция должна быть объявлена раньше, чем ее можно будет использовать. К сожалению, в Bash нет возможности «опережающего объявления» функции, как например в C.
Допускается даже создание вложенных функций, хотя пользы от этого немного.
Объявление функции может размещаться в самых неожиданных местах.
22.1. Сложные функции и сложности с функциями
Функции могут принимать входные аргументы и возвращать код завершения.
Доступ к входным аргументам, в функциях, производится посредством позиционных параметров, т.е. $1, $2 и так далее.
Пример 22-2. Функция с аргументами
Команда shift вполне применима и к аргументам функций (см. Пример 33-10).
В отличие от других языков программирования, в сценариях на языке командной оболочке, в функции передаются аргументы по значению. [1] Если имена переменных (которые фактически являются указателями) передаются функции в виде аргументов, то они интерпретируются как обычные строки символов и не могут быть разыменованы. Функции интерпретируют свои аргументы буквально.
Exit и Return
Функции возвращают значение в виде кода завершения. Код завершения может быть задан явно, с помощью команды return, в противном случае будет возвращен код завершения последней команды в функции ( 0 — в случае успеха, иначе — ненулевой код ошибки). Код завершения в сценарии может быть получен через переменную $?.
Завершает исполнение функции. Команда return [2] может иметь необязательный аргумент типа integer , который возвращается в вызывающий сценарий как «код завершения» функции, это значение так же записывается в переменную $?.
Пример 22-3. Наибольшее из двух чисел
Для случаев, когда функция должна возвращать строку или массив, используйте специальные переменные.
Пример 22-4. Преобразование чисел в римскую форму записи
Наибольшее положительное целое число, которое может вернуть функция — 255. Команда return очень тесно связана с понятием код завершения, что объясняет это специфическое ограничение. К счастью существуют различные способы преодоления этого ограничения.
Пример 22-5. Проверка возможности возврата функциями больших значений
Как видно из примера, функции могут возвращать большие отрицательные значения (имеются ввиду — большие по своему абсолютному значению, прим. перев.). Используя эту особенность, можно обыграть возможность получения от функций большие положительные значения.
Еще один способ — использовать глобальные переменные для хранения «возвращаемого значения» .
Пример 22-6. Сравнение двух больших целых чисел
Упражнение: Используя только что полученные знания, добавьте в предыдущий пример, преобразования чисел в римскую форму записи, возможность обрабатывать большие числа.
Перенаправление
Перенаправление ввода для функций
Функции — суть есть блок кода, а это означает, что устройство stdin для функций может быть переопределено (перенаправление stdin) (как в Пример 3-1).
Пример 22-7. Настоящее имя пользователя
Ниже приводится альтернативный, и возможно менее запутанный, способ перенаправления ввода для функций. Он заключается в использовании перенаправления ввода для блока кода, заключенного в фигурные скобки, в пределах функции.
Примечания
Механизм косвенных ссылок на переменные (см. Пример 34-2) слишком неудобен для передачи аргументов по ссылке.
Команда return — это встроенная команда Bash.
Источник
Bash Функции
Bash Functions
В этом руководстве мы рассмотрим основы функций Bash и покажем, как их использовать в сценариях оболочки.
Функция Bash — это, по сути, набор команд, которые можно вызывать множество раз. Цель функции — помочь вам сделать ваши bash-скрипты более читабельными и избежать повторного написания одного и того же кода. По сравнению с большинством языков программирования, функции Bash несколько ограничены.
Определение функций Bash
Синтаксис объявления функции bash прост. Функции могут быть объявлены в двух разных форматах:
Первый формат начинается с имени функции, за которым следуют скобки. Это предпочтительный и более используемый формат.
Второй формат начинается с зарезервированного слова function , за которым следует имя функции.
Несколько моментов, которые следует отметить:
Команды между фигурными скобками ( <> ) называются телом функции. Фигурные скобки должны быть отделены от тела пробелами или переводами строки.
Определение функции не выполняет ее. Чтобы вызвать функцию bash, просто используйте имя функции. Команды между фигурными скобками выполняются всякий раз, когда функция вызывается в сценарии оболочки.
Определение функции должно быть помещено перед любыми вызовами функции.
При использовании однострочных «уплотненных» функций точка с запятой ; должна следовать за последней командой в функции.
Всегда старайтесь, чтобы имена ваших функций были описательными.
Чтобы лучше это понять, взглянем на следующий пример:
Давайте проанализируем код построчно:
В строке 3 мы определяем функцию, присваивая ей имя. Фигурная скобка < отмечает начало тела функции.
Линия 4 — это тело функции. Тело функции может содержать несколько команд, операторов и объявлений переменных.
Линия 5 , закрывающая фигурная скобка > , определяет конец hello_world функции.
В строке 7 мы выполняем функцию. Вы можете выполнять функцию столько раз, сколько вам нужно.
Если вы запустите скрипт, он напечатает hello, world .
Область действия переменных
Глобальные переменные — это переменные, к которым можно получить доступ из любого места в скрипте независимо от области видимости. В Bash все переменные по умолчанию определены как глобальные, даже если они объявлены внутри функции.
Локальные переменные могут быть объявлены в теле функции с local ключевым словом и могут использоваться только внутри этой функции. Вы можете иметь локальные переменные с одинаковыми именами в разных функциях.
Чтобы лучше проиллюстрировать, как работает область видимости переменных в Bash, давайте рассмотрим этот пример:
Сценарий начинается с определения двух глобальных переменных var1 и var2 . Затем есть функция, которая устанавливает локальную переменную var1 и изменяет глобальную переменную var2 .
Если вы запустите скрипт, вы должны увидеть следующий вывод:
Из приведенного выше вывода можно сделать вывод, что:
Когда локальная переменная устанавливается внутри тела функции с тем же именем, что и существующая глобальная переменная, она будет иметь приоритет над глобальной переменной.
Глобальные переменные могут быть изменены внутри функции.
Возвращаемые значения
В отличие от функций в «реальных» языках программирования, функции Bash не позволяют вам возвращать значение при вызове. Когда функция bash завершает свою работу, ее возвращаемое значение является состоянием последнего оператора, выполненного в функции, 0 для успеха и ненулевого десятичного числа в диапазоне 1 — 255 для отказа.
Статус возврата можно указать с помощью return ключевого слова, и оно присваивается переменной $? . return Оператор завершает функцию. Вы можете думать об этом как о состоянии выхода из функции .
Чтобы на самом деле вернуть произвольное значение из функции, нам нужно использовать другие методы. Самый простой вариант — присвоить результат функции глобальной переменной:
Другой, лучший вариант для возврата значения из функции отправить значение с stdout помощью эха или , printf как показано ниже:
Вместо того, чтобы просто выполнять функцию, которая будет печатать сообщение на стандартный вывод, мы назначаем вывод функции func_result переменной, используя $() подстановку команд. Переменная может позже использоваться по мере необходимости.
Передача аргументов в функции Bash
Чтобы передать любое количество аргументов функции bash, просто поместите их сразу после имени функции, разделив их пробелом. Хорошей практикой является двойная кавычка аргументов, чтобы избежать неправильного разбора аргумента с пробелами в нем.
Передаваемые параметры $1 , $2 , $3 . $n , соответствующее положению параметра после имени функции.
$0 Переменная зарезервирована для имени функции.
$# Переменная содержит число позиционных параметров / аргументов , передаваемых функции.
$* И $@ переменные содержат все позиционные параметры / аргументы , переданные функции.
При двойных кавычках «$*» раскрывается в одну строку, разделенную пробелом (первый символ IFS) — «$1 $2 $n» .
При двойных кавычках «$@» расширяется до отдельных строк — «$1» «$2» «$n» .
Когда не двойные кавычки, $* а $@ одинаковые.
Вывод
Функция Bash — это блок многократно используемого кода, предназначенный для выполнения определенной операции. После определения функция может вызываться несколько раз в скрипте.
Вы также можете прочитать о том, как использовать функцию Bash для создания запоминающейся команды быстрого доступа для более длинной команды.
Источник
Bash-скрипты: начало
Сегодня поговорим о bash-скриптах. Это — сценарии командной строки, написанные для оболочки bash. Существуют и другие оболочки, например — zsh, tcsh, ksh, но мы сосредоточимся на bash. Этот материал предназначен для всех желающих, единственное условие — умение работать в командной строке Linux.
Сценарии командной строки — это наборы тех же самых команд, которые можно вводить с клавиатуры, собранные в файлы и объединённые некоей общей целью. При этом результаты работы команд могут представлять либо самостоятельную ценность, либо служить входными данными для других команд. Сценарии — это мощный способ автоматизации часто выполняемых действий.
Итак, если говорить о командной строке, она позволяет выполнить несколько команд за один раз, введя их через точку с запятой:
На самом деле, если вы опробовали это в своём терминале, ваш первый bash-скрипт, в котором задействованы две команды, уже написан. Работает он так. Сначала команда pwd выводит на экран сведения о текущей рабочей директории, потом команда whoami показывает данные о пользователе, под которым вы вошли в систему.
Используя подобный подход, вы можете совмещать сколько угодно команд в одной строке, ограничение — лишь в максимальном количестве аргументов, которое можно передать программе. Определить это ограничение можно с помощью такой команды:
Командная строка — отличный инструмент, но команды в неё приходится вводить каждый раз, когда в них возникает необходимость. Что если записать набор команд в файл и просто вызывать этот файл для их выполнения? Собственно говоря, тот файл, о котором мы говорим, и называется сценарием командной строки.
Как устроены bash-скрипты
Создайте пустой файл с использованием команды touch . В его первой строке нужно указать, какую именно оболочку мы собираемся использовать. Нас интересует bash , поэтому первая строка файла будет такой:
В других строках этого файла символ решётки используется для обозначения комментариев, которые оболочка не обрабатывает. Однако, первая строка — это особый случай, здесь решётка, за которой следует восклицательный знак (эту последовательность называют шебанг) и путь к bash , указывают системе на то, что сценарий создан именно для bash .
Команды оболочки отделяются знаком перевода строки, комментарии выделяют знаком решётки. Вот как это выглядит:
Тут, так же, как и в командной строке, можно записывать команды в одной строке, разделяя точкой с запятой. Однако, если писать команды на разных строках, файл легче читать. В любом случае оболочка их обработает.
Установка разрешений для файла сценария
Сохраните файл, дав ему имя myscript , и работа по созданию bash-скрипта почти закончена. Сейчас осталось лишь сделать этот файл исполняемым, иначе, попытавшись его запустить, вы столкнётесь с ошибкой Permission denied .
Попытка запуска файла сценария с неправильно настроенными разрешениями
Сделаем файл исполняемым:
Теперь попытаемся его выполнить:
После настройки разрешений всё работает как надо.
Успешный запуск bash-скрипта
Вывод сообщений
Для вывода текста в консоль Linux применяется команда echo . Воспользуемся знанием этого факта и отредактируем наш скрипт, добавив пояснения к данным, которые выводят уже имеющиеся в нём команды:
Вот что получится после запуска обновлённого скрипта.
Вывод сообщений из скрипта
Теперь мы можем выводить поясняющие надписи, используя команду echo . Если вы не знаете, как отредактировать файл, пользуясь средствами Linux, или раньше не встречались с командой echo , взгляните на этот материал.
Использование переменных
Переменные позволяют хранить в файле сценария информацию, например — результаты работы команд для использования их другими командами.
Нет ничего плохого в исполнении отдельных команд без хранения результатов их работы, но возможности такого подхода весьма ограничены.
Существуют два типа переменных, которые можно использовать в bash-скриптах:
Переменные среды
Пользовательские переменные
Переменные среды
Иногда в командах оболочки нужно работать с некими системными данными. Вот, например, как вывести домашнюю директорию текущего пользователя:
Обратите внимание на то, что мы можем использовать системную переменную $HOME в двойных кавычках, это не помешает системе её распознать. Вот что получится, если выполнить вышеприведённый сценарий.
Использование переменной среды в сценарии
А что если надо вывести на экран значок доллара? Попробуем так:
Система обнаружит знак доллара в строке, ограниченной кавычками, и решит, что мы сослались на переменную. Скрипт попытается вывести на экран значение неопределённой переменной $1 . Это не то, что нам нужно. Что делать?
В подобной ситуации поможет использование управляющего символа, обратной косой черты, перед знаком доллара:
Теперь сценарий выведет именно то, что ожидается.
Использование управляющей последовательности для вывода знака доллара
Пользовательские переменные
В дополнение к переменным среды, bash-скрипты позволяют задавать и использовать в сценарии собственные переменные. Подобные переменные хранят значение до тех пор, пока не завершится выполнение сценария.
Как и в случае с системными переменными, к пользовательским переменным можно обращаться, используя знак доллара:
Вот что получится после запуска такого сценария.
Пользовательские переменные в сценарии
Подстановка команд
Одна из самых полезных возможностей bash-скриптов — это возможность извлекать информацию из вывода команд и назначать её переменным, что позволяет использовать эту информацию где угодно в файле сценария.
Сделать это можно двумя способами.
С помощью значка обратного апострофа «`»
С помощью конструкции $()
Используя первый подход, проследите за тем, чтобы вместо обратного апострофа не ввести одиночную кавычку. Команду нужно заключить в два таких значка:
При втором подходе то же самое записывают так:
А скрипт, в итоге, может выглядеть так:
В ходе его работы вывод команды pwd будет сохранён в переменной mydir , содержимое которой, с помощью команды echo , попадёт в консоль.
Скрипт, сохраняющий результаты работы команды в переменной
Математические операции
Для выполнения математических операций в файле скрипта можно использовать конструкцию вида $((a+b)) :
Математические операции в сценарии
Управляющая конструкция if-then
В некоторых сценариях требуется управлять потоком исполнения команд. Например, если некое значение больше пяти, нужно выполнить одно действие, в противном случае — другое. Подобное применимо в очень многих ситуациях, и здесь нам поможет управляющая конструкция if-then . В наиболее простом виде она выглядит так:
А вот рабочий пример:
В данном случае, если выполнение команды pwd завершится успешно, в консоль будет выведен текст «it works».
Воспользуемся имеющимися у нас знаниями и напишем более сложный сценарий. Скажем, надо найти некоего пользователя в /etc/passwd , и если найти его удалось, сообщить о том, что он существует.
Вот что получается после запуска этого скрипта.
Здесь мы воспользовались командой grep для поиска пользователя в файле /etc/passwd . Если команда grep вам незнакома, её описание можно найти здесь.
В этом примере, если пользователь найден, скрипт выведет соответствующее сообщение. А если найти пользователя не удалось? В данном случае скрипт просто завершит выполнение, ничего нам не сообщив. Хотелось бы, чтобы он сказал нам и об этом, поэтому усовершенствуем код.
Управляющая конструкция if-then-else
Для того, чтобы программа смогла сообщить и о результатах успешного поиска, и о неудаче, воспользуемся конструкцией if-then-else . Вот как она устроена:
Если первая команда возвратит ноль, что означает её успешное выполнение, условие окажется истинным и выполнение не пойдёт по ветке else . В противном случае, если будет возвращено что-то, отличающееся от нуля, что будет означать неудачу, или ложный результат, будут выполнены команды, расположенные после else .
Напишем такой скрипт:
Его исполнение пошло по ветке else .
Запуск скрипта с конструкцией if-then-else
Ну что же, продолжаем двигаться дальше и зададимся вопросом о более сложных условиях. Что если надо проверить не одно условие, а несколько? Например, если нужный пользователь найден, надо вывести одно сообщение, если выполняется ещё какое-то условие — ещё одно сообщение, и так далее. В подобной ситуации нам помогут вложенные условия. Выглядит это так:
Если первая команда вернёт ноль, что говорит о её успешном выполнении, выполнятся команды в первом блоке then , иначе, если первое условие окажется ложным, и если вторая команда вернёт ноль, выполнится второй блок кода.
В подобном скрипте можно, например, создавать нового пользователя с помощью команды useradd , если поиск не дал результатов, или делать ещё что-нибудь полезное.
Сравнение чисел
В скриптах можно сравнивать числовые значения. Ниже приведён список соответствующих команд.
n1 -eq n2 Возвращает истинное значение, если n1 равно n2 . n1 -ge n2 Возвращает истинное значение, если n1 больше или равно n2 . n1 -gt n2 Возвращает истинное значение, если n1 больше n2 . n1 -le n2 Возвращает истинное значение, если n1 меньше или равно n2 . n1 -lt n2 Возвращает истинное значение, если n1 меньше n2 . n1 -ne n2 Возвращает истинное значение, если n1 не равно n2 .
В качестве примера опробуем один из операторов сравнения. Обратите внимание на то, что выражение заключено в квадратные скобки.
Вот что выведет эта команда.
Сравнение чисел в скриптах
Значение переменной val1 больше чем 5, в итоге выполняется ветвь then оператора сравнения и в консоль выводится соответствующее сообщение.
Сравнение строк
В сценариях можно сравнивать и строковые значения. Операторы сравнения выглядят довольно просто, однако у операций сравнения строк есть определённые особенности, которых мы коснёмся ниже. Вот список операторов.
str1 = str2 Проверяет строки на равенство, возвращает истину, если строки идентичны. s tr1 != str2 Возвращает истину, если строки не идентичны. str1 Возвращает истину, если str1 меньше, чем str2 . str1 > str2 Возвращает истину, если str1 больше, чем str2 . -n str1 Возвращает истину, если длина str1 больше нуля. -z str1 Возвращает истину, если длина str1 равна нулю.
Вот пример сравнения строк в сценарии:
В результате выполнения скрипта получим следующее.
Сравнение строк в скриптах
Вот одна особенность сравнения строк, о которой стоит упомянуть. А именно, операторы «>» и « » как команду перенаправления вывода.
Вот как работа с этими операторами выглядит в коде:
Вот результаты работы скрипта.
Сравнение строк, выведенное предупреждение
Обратите внимание на то, что скрипт, хотя и выполняется, выдаёт предупреждение:
Для того, чтобы избавиться от этого предупреждения, заключим $val2 в двойные кавычки:
Теперь всё работает как надо.
Ещё одна особенность операторов «>» и « myfile , после чего выполним в терминале такую команду:
Она отсортирует строки из файла так:
Команда sort , по умолчанию, сортирует строки по возрастанию, то есть строчная буква в нашем примере меньше прописной. Теперь подготовим скрипт, который будет сравнивать те же строки:
Если его запустить, окажется, что всё наоборот — строчная буква теперь больше прописной.
Команда sort и сравнение строк в файле сценария
В командах сравнения прописные буквы меньше строчных. Сравнение строк здесь выполняется путём сравнения ASCII-кодов символов, порядок сортировки, таким образом, зависит от кодов символов.
Команда sort , в свою очередь, использует порядок сортировки, заданный в настройках системного языка.
Проверки файлов
Пожалуй, нижеприведённые команды используются в bash-скриптах чаще всего. Они позволяют проверять различные условия, касающиеся файлов. Вот список этих команд.
-d file Проверяет, существует ли файл, и является ли он директорией. -e file Проверяет, существует ли файл. -f file Проверяет, существует ли файл, и является ли он файлом. -r file Проверяет, существует ли файл, и доступен ли он для чтения. -s file П роверяет, существует ли файл, и не является ли он пустым. -w file Проверяет, существует ли файл, и доступен ли он для записи. -x file Проверяет, существует ли файл, и является ли он исполняемым. file1 -nt file2 Проверяет, новее ли file1 , чем file2 . file1 -ot file2 Проверяет, старше ли file1 , чем file2 . -O file Проверяет, существует ли файл, и является ли его владельцем текущий пользователь. -G file Проверяет, существует ли файл, и соответствует ли его идентификатор группы идентификатору группы текущего пользователя.
Эти команды, как впрочем, и многие другие рассмотренные сегодня, несложно запомнить. Их имена, являясь сокращениями от различных слов, прямо указывают на выполняемые ими проверки.
Опробуем одну из команд на практике:
Этот скрипт, для существующей директории, выведет её содержимое.
Вывод содержимого директории
Полагаем, с остальными командами вы сможете поэкспериментировать самостоятельно, все они применяются по тому же принципу.
Итоги
Сегодня мы рассказали о том, как приступить к написанию bash-скриптов и рассмотрели некоторые базовые вещи. На самом деле, тема bash-программирования огромна. Эта статья является переводом первой части большой серии из 11 материалов. Если вы хотите продолжения прямо сейчас — вот список оригиналов этих материалов. Для удобства сюда включён и тот, перевод которого вы только что прочли.
Bash Script Step By Step — здесь речь идёт о том, как начать создание bash-скриптов, рассмотрено использование переменных, описаны условные конструкции, вычисления, сравнения чисел, строк, выяснение сведений о файлах.
Bash Scripting Part 2, Bash the awesome — тут раскрываются особенности работы с циклами for и while.
Bash Scripting Part 3, Parameters & options — этот материал посвящён параметрам командной строки и ключам, которые можно передавать скриптам, работе с данными, которые вводит пользователь, и которые можно читать из файлов.
Bash Scripting Part 4, Input & Output — здесь речь идёт о дескрипторах файлов и о работе с ними, о потоках ввода, вывода, ошибок, о перенаправлении вывода.
Bash Scripting Part 5, Sighals & Jobs — этот материал посвящён сигналам Linux, их обработке в скриптах, запуску сценариев по расписанию.
Bash Scripting Part 6, Functions — тут можно узнать о создании и использовании функций в скриптах, о разработке библиотек.
Bash Scripting Part 7, Using sed — эта статья посвящена работе с потоковым текстовым редактором sed.
Bash Scripting Part 8, Using awk — данный материал посвящён программированию на языке обработки данных awk.
Bash Scripting Part 9, Regular Expressions — тут можно почитать об использовании регулярных выражений в bash-скриптах.
Bash Scripting Part 10, Practical Examples — здесь приведены приёмы работы с сообщениями, которые можно отправлять пользователям, а так же методика мониторинга диска.
Bash Scripting Part 11, Expect Command — этот материал посвящён средству Expect, с помощью которого можно автоматизировать взаимодействие с интерактивными утилитами. В частности, здесь идёт речь об expect-скриптах и об их взаимодействии с bash-скриптами и другими программами.
Полагаем, одно из ценных свойств этой серии статей заключается в том, что она, начинаясь с самого простого, подходящего для пользователей любого уровня, постепенно ведёт к довольно серьёзным темам, давая шанс всем желающим продвинуться в деле создания сценариев командной строки Linux.
Уважаемые читатели! Просим гуру bash-программирования рассказать о том, как они добрались до вершин мастерства, поделиться секретами, а от тех, кто только что написал свой первый скрипт, ждём впечатлений.