Linux Tools: shells, ash #5 - ash syntax, functions
linux linux-tools shell ashВ прошлой статье прошли по сложным командам, циклам и условиям. Теперь усложняем. Что если нужно выполнять много сложных команд и еще хочется переиспользовать некоторые части команд?
На помощь приходят функции
name () {
command
...
return [exitstatus]
}
Функции могут содержать всего одну команду и записываться без фигурных скобок
$ hello() echo 'Hello'
$ hello
Hello
Или несколько команд
$ hello() {
echo 'Hello'
echo 'World'
}
$ hello
Hello
World
Каждая функция работает как обычная команда-программа и возвращает exit-код. По умолчанию возвращается 0, но можно вернуть нужный exit-код c помощью return. При этом выполнение функции прекращается на return.
$ hello() {
echo 'Hello'
return 100
echo 'World'
}
# World не выводится
$ hello
Hello
# $? содержит exit-код предыдущей команды
$ echo $?
100
Параметры и переменные #
В 3 статье серии мы уже немного говорили о переменных, сегодня разберемся с ними подробно.
Параметры (Parameters) могут позиционными и специальными, а параметры с именем называются переменными (Variables).
Позиционные параметры - Positional Parameters #
При запуске скрипта все параметры перечисленные после команды являются позиционными параметрами и ash позволяет обращаться к ним по номерам, начиная с 1.
В параметре $0
хранится путь к текущему скрипту или путь к shell, если запущен интерактивный режим
# параметр $0 самого shell хранит путь к shell-программе
$ echo $0
/usr/bin/ash
$ cat ./test.sh
echo '$0='$0
echo '$1='$1
echo '$2='$2
# передаем пару параметров скрипту test.sh
$ ash ./test.sh first second
$0=./test.sh
$1=first
$2=second
Точно такая же логика с позиционными параметрами работает и для функций. В $0
будет содержаться путь к shell, а параметры в функции можно передавать аналогично скриптам
$ hello() { echo '$0='$0; echo "Hello, $1"; }
$ hello World
$0=/usr/bin/ash
Hello, World
Позиционные параметры в скриптах и функциях можно переопределять с помощью builtin-команды set
(подробно синтаксис set разберем в отдельной статье про builtin-команды)
$ hello() {
echo "Hello, $1 and $2";
set Beavis Butthead;
echo "Hello, $1 and $2";
}
$ hello Rick Morty
Hello, Rick and Morty
Hello, Beavis and Butthead
Переменные - Variables #
Shell-переменные можно использовать внутри скриптов для хранения значений
$ a=1
$ b=2
$ TEST=100500
$ echo "$a-$b-$TEST"
1-2-100500
Но эти параметры не передаются при вызовах команд в environment-переменные, можно делать вручную задавать environment-переменные для каждой команды
$ cat ./test.sh
echo '$TEST='$TEST
# переменная TEST будет пуста
$ ./test.sh
$TEST=
# передаем значение переменной TEST на момент вызова
$ TEST=1 ./test.sh
$TEST=1
Для того чтобы shell-переменные передавались в окружение (environment) всем выполняемым командам, нужно экспортировать переменную
$ export TEST=100500
$ ./test.sh
$TEST=100500
Переменные в функциях #
В функциях видны все shell-переменные (и environment-переменные тоже) — их можно использовать внутри.
Также все команды, выполненные внутри функций влияют на shell, в общем все точно также как и при выполнении обычных команд без функции. Чтобы команды не влияли на текущий shell можно группировать их в круглых скобках .
Локальные переменные в функциях #
Если определенные переменные нужны только внутри функции можно воспользоваться builtin-функцией local.
Команда local
должна быть вызвана в начале функции и должна содержать имя локальной переменной. Локальные переменные никак не инициализируются и задавать значения им нужно отдельно.
# в shell своя переменная name
$ name=Pachino
$ hello () {
local name # в функции своя переменная name
echo 'local name='$name
name=DeNiro
echo 'local name='$name
}
$ hello
local name=Pachino
local name=DeNiro
# в shell переменная не поменялась
$ echo 'name='$name
name=Pachino
Кроме этого scope локальных переменных сохраняется при создании вложенных функций - то есть для вложенной функции будет своя переменная, а не глобальная.
$ name=Pachino
$ hello () {
local name
name=DeNiro
echo 'name='$name
bang() { echo 'bang name='$name }
bang
}
$ hello
name=DeNiro
# в функции bang переиспользуется локальная переменная name
# из функции hello
bang name=DeNiro
# в shell осталась своя переменная
$ echo $name
Pachino
Вместо имени переменной в local
можно указать -
$ hello() {
local -
# ...
}
В этом случае все установленные опции с помощью команды set
будут локальными и вернуться к своим значениям при выходе из функции. Про установку опций с помощью set
говорили во 2 статье серии.
Потоки в функциях #
Раз функция ведет себя как команда, значит внутри нее можно работать с потоками и использовать функции в пайплайнах
# передаем в cat данные из stdin-потока
$ hello { echo 'Hello'; cat <&0; }
$ echo 'World' | hello
Hello
World
stdout и stderr- потоки у функции также свои
$ hello() { echo 'Hello' >&1; echo 'World' >&2; }
$ hello
Hello
World
# перенаправляем stderr в /dev/null
$ hello 2>/dev/null
Hello
Итого #
Сегодня узнали как создавать shell-функции и как использовать параметры, потоки и переменные внутри shell-функций.
Основной функционал ash мы рассмотрели, но существует несколько возможностей, которые делают написание скриптов еще удобнее – их и обсудим в следующей статье.
Предыдущая статья: shells, ash #4 - ash syntax, complex commands, pipelines, conditions and loops
Следующая статья: shells, ash #6 - environment variables