Bash-скрипты, часть 6: функции и разработка библиотек +44
Bash-скрипты: начало
Bash-скрипты, часть 2: циклы
Bash-скрипты, часть 3: параметры и ключи командной строки
Bash-скрипты, часть 4: ввод и вывод
Bash-скрипты, часть 5: сигналы, фоновые задачи, управление сценариями
Bash-скрипты, часть 6: функции и разработка библиотек
Bash-скрипты, часть 7: sed и обработка текстов
Bash-скрипты, часть 8: язык обработки данных awk
Bash-скрипты, часть 9: регулярные выражения
Занимаясь разработкой bash-скриптов, вы рано или поздно столкнётесь с тем, что вам периодически приходится использовать одни и те же фрагменты кода. Постоянно набирать их вручную скучно, а копирование и вставка — не наш метод. Как быть? Хорошо бы найти средство, которое позволяет один раз написать блок кода и, когда он понадобится снова, просто сослаться на него в скрипте.
Оболочка bash предоставляет такую возможность, позволяя создавать функции. Функции bash — это именованные блоки кода, которые можно повторно использовать в скриптах.
Объявление функций
Функцию можно объявить так:
functionName {
}
Или так:
functionName() {
}
Функцию можно вызвать без аргументов и с аргументами.
Использование функций
Напишем скрипт, содержащий объявление функции и использующий её:
#!/bin/bash
function myfunc {
echo "This is an example of using a function"
}
count=1
while [ $count -le 3 ]
do
myfunc
count=$(( $count + 1 ))
done
echo "This is the end of the loop"
myfunc
echo "End of the script"
Здесь создана функция с именем
myfunc
. Для вызова функции достаточно указать её имя.Результаты вызова функции
Функцию можно вызывать столько раз, сколько нужно. Обратите внимание на то, что попытавшись использовать функцию до её объявления, вы столкнётесь с ошибкой. Напишем демонстрирующий это скрипт:
#!/bin/bash
count=1
while [ $count -le 3 ]
do
myfunc
count=$(( $count + 1 ))
done
echo "This is the end of the loop"
function myfunc {
echo "This is an example of using a function"
}
echo "End of the script"
Как и ожидается, ничего хорошего после его запуска не произошло.
Попытка воспользоваться функцией до её объявления
Придумывая имена для функций, учитывайте то, что они должны быть уникальными, иначе проблем не избежать. Если вы переопределите ранее объявленную функцию, новая функция будет вызываться вместо старой без каких-либо уведомлений или сообщений об ошибках. Продемонстрируем это на примере:
#!/bin/bash
function myfunc {
echo "The first function definition"
}
myfunc
function myfunc {
echo "The second function definition"
}
myfunc
echo "End of the script"
Как видно, новая функция преспокойно затёрла старую.
Переопределение функции
Использование команды return
Команда
return
позволяет задавать возвращаемый функцией целочисленный код завершения. Есть два способа работы с тем, что является результатом вызова функции. Вот первый:#!/bin/bash
function myfunc {
read -p "Enter a value: " value
echo "adding value"
return $(( $value + 10 ))
}
myfunc
echo "The new value is $?"
Команда
echo
вывела сумму введённого числа и числа 10.Вывод значения, возвращаемого функцией
Функция
myfunc
добавляет 10 к числу, которое содержится в переменной $value
, значение которой задаёт пользователь во время работы сценария. Затем она возвращает результат, используя команду return
. То, что возвратила функция, выводится командой echo
с использованием переменной $?
.Если вы выполните любую другую команду до извлечения из переменной
$?
значения, возвращённого функцией, это значение будет утеряно. Дело в том, что данная переменная хранит код возврата последней выполненной команды.Учтите, что максимальное число, которое может вернуть команда
return
— 255. Если функция должна возвращать большее число или строку, понадобится другой подход.Запись вывода функции в переменную
Ещё один способ возврата результатов работы функции заключается в записи данных, выводимых функцией, в переменную. Такой подход позволяет обойти ограничения команды
return
и возвращать из функции любые данные. Рассмотрим пример:#!/bin/bash
function myfunc {
read -p "Enter a value: " value
echo $(( $value + 10 ))
}
result=$( myfunc)
echo "The value is $result"
Вот что получится после вызова данного скрипта.
Запись результатов работы функции в переменную
Аргументы функций
Функции bash можно воспринимать как небольшие фрагменты кода, которые позволяют экономить время и место, избавляя нас от необходимости постоянно вводить с клавиатуры или копировать одни и те же наборы команд. Однако, возможности функций гораздо шире. В частности, речь идёт о передаче им аргументов.
Функции могут использовать стандартные позиционные параметры, в которые записывается то, что передаётся им при вызове. Например, имя функции хранится в параметре
$0
, первый переданный ей аргумент — в $1
, второй — в $2
, и так далее. Количество переданных функции аргументов можно узнать, обратившись к переменной $#
. Если вы знакомы с третьей частью этого цикла материалов, вы не можете не заметить, что всё это очень похоже на то, как скрипты обрабатывают переданные им параметры командной строки.Аргументы передают функции, записывая их после её имени:
myfunc $val1 10 20
Вот пример, в котором функция вызывается с аргументами и занимается их обработкой:
#!/bin/bash
function addnum {
if [ $# -eq 0 ] || [ $# -gt 2 ]
then
echo -1
elif [ $# -eq 1 ]
then
echo $(( $1 + $1 ))
else
echo $(( $1 + $2 ))
fi
}
echo -n "Adding 10 and 15: "
value=$(addnum 10 15)
echo $value
echo -n "Adding one number: "
value=$(addnum 10)
echo $value
echo -n "Adding no numbers: "
value=$(addnum)
echo $value
echo -n "Adding three numbers: "
value=$(addnum 10 15 20)
echo $value
Запустим скрипт.
Вызов функции с аргументами
Функция
addnum
проверяет число переданных ей при вызове из скрипта аргументов. Если их нет, или их больше двух, функция возвращает значение -1. Если параметр всего один, она прибавляет его к нему самому и возвращает результат. Если параметров два, функция складывает их.Обратите внимание на то, что функция не может напрямую работать с параметрами, которые переданы скрипту при его запуске из командной строки. Например, напишем такой сценарий:
#!/bin/bash
function myfunc {
echo $(( $1 + $2 ))
}
if [ $# -eq 2 ]
then
value=$( myfunc)
echo "The result is $value"
else
echo "Usage: myfunc a b"
fi
При его запуске, а точнее, при вызове объявленной в нём функции, будет выведено сообщение об ошибке.
Функция не может напрямую использовать параметры, переданные сценарию
Вместо этого, если в функции планируется использовать параметры, переданные скрипту при вызове из командной строки, надо передать их ей при вызове:
#!/bin/bash
function myfunc {
echo $(( $1 + $2 ))
}
if [ $# -eq 2 ]
then
value=$(myfunc $1 $2)
echo "The result is $value"
else
echo "Usage: myfunc a b"
fi
Теперь всё работает правильно.
Передача функции параметров, с которыми запущен скрипт
Работа с переменными в функциях
Переменные, которыми мы пользуемся в сценариях, характеризуются областью видимости. Это — те места кода, из которых можно работать с этими переменными. Переменные, объявленные внутри функций, ведут себя не так, как те переменные, с которыми мы уже сталкивались. Они могут быть скрыты от других частей скриптов.
Существуют два вида переменных:
- Глобальные переменные.
- Локальные переменные.
?Глобальные переменные
Глобальные переменные — это переменные, которые видны из любого места bash-скрипта. Если вы объявили глобальную переменную в основном коде скрипта, к такой переменной можно обратиться из функции.
Почти то же самое справедливо и для глобальных переменных, объявленных в функциях. Обращаться к ним можно и в основном коде скрипта после вызова функций.
По умолчанию все объявленные в скриптах переменные глобальны. Так, к переменным, объявленным за пределами функций, можно без проблем обращаться из функций:
#!/bin/bash
function myfunc {
value=$(( $value + 10 ))
}
read -p "Enter a value: " value
myfunc
echo "The new value is: $value"
Вот что выведет этот сценарий.
Обращение к глобальной переменной из функции
Когда переменной присваивается новое значение в функции, это новое значение не теряется когда скрипт обращается к ней после завершения работы функции. Именно это можно видеть в предыдущем примере.
Что если такое поведение нас не устраивает? Ответ прост — надо использовать локальные переменные.
?Локальные переменные
Переменные, которые объявляют и используют внутри функции, могут быть объявлены локальными. Для того, чтобы это сделать, используется ключевое слово
local
перед именем переменной:local temp=$(( $value + 5 ))
Если за пределами функции есть переменная с таким же именем, это на неё не повлияет. Ключевое слово
local
позволяет отделить переменные, используемые внутри функции, от остальных переменных. Рассмотрим пример:#!/bin/bash
function myfunc {
local temp=$[ $value + 5 ]
echo "The Temp from inside function is $temp"
}
temp=4
myfunc
echo "The temp from outside is $temp"
Запустим скрипт.
Локальная переменная в функции
Здесь, когда мы работаем с переменной
$temp
внутри функции, это не влияет на значение, назначенное переменной с таким же именем за её пределами.Передача функциям массивов в качестве аргументов
Попробуем передать функции в качестве аргумента массив. Сразу хочется сказать, что работать такая конструкция будет неправильно:
#!/bin/bash
function myfunc {
echo "The parameters are: $@"
arr=$1
echo "The received array is ${arr[*]}"
}
myarray=(1 2 3 4 5)
echo "The original array is: ${myarray[*]}"
myfunc $myarray
Неправильный подход к передаче функциям массивов
Как видно из примера, при передаче функции массива, она получит доступ лишь к его первому элементу.
Для того, чтобы эту проблему решить, из массива надо извлечь имеющиеся в нём данные и передать их функции как самостоятельные аргументы. Если надо, внутри функции полученные ей аргументы можно снова собрать в массив:
#!/bin/bash
function myfunc {
local newarray
newarray=("$@")
echo "The new array value is: ${newarray[*]}"
}
myarray=(1 2 3 4 5)
echo "The original array is ${myarray[*]}"
myfunc ${myarray[*]}
Запустим сценарий.
Сборка массива внутри функции
Как видно из примера, функция собрала массив из переданных ей аргументов.
Рекурсивные функции
Рекурсия — это когда функция сама себя вызывает. Классический пример рекурсии — функция для вычисления факториала. Факториал числа — это произведение всех натуральных чисел от 1 до этого числа. Например, факториал 5 можно найти так:
5! = 1 * 2 * 3 * 4 * 5
Если формулу вычисления факториала написать в рекурсивном виде, получится следующее:
x! = x * (x-1)!
Этой формулой можно воспользоваться для того, чтобы написать рекурсивную функцию:
#!/bin/bash
function factorial {
if [ $1 -eq 1 ]
then
echo 1
else
local temp=$(( $1 - 1 ))
local result=$(factorial $temp)
echo $(( $result * $1 ))
fi
}
read -p "Enter value: " value
result=$(factorial $value)
echo "The factorial of $value is: $result"
Проверим, верно ли работает этот скрипт.
Вычисление факториала
Как видите, всё работает как надо.
Создание и использование библиотек
Итак, теперь вы знаете, как писать функции и как вызывать их в том же скрипте, где они объявлены. Что если надо использовать функцию, тот блок кода, который она собой представляет, в другом скрипте, не используя копирование и вставку?
Оболочка bash позволяет создавать так называемые библиотеки — файлы, содержащие функции, а затем использовать эти библиотеки в любых скриптах, где они нужны.
Ключ к использованию библиотек — в команде
source
. Эта команда используется для подключения библиотек к скриптам. В результате функции, объявленные в библиотеке, становятся доступными в скрипте, в противном же случае функции из библиотек не будут доступны в области видимости других скриптов.У команды
source
есть псевдоним — оператор «точка». Для того, чтобы подключить файл в скрипте, в скрипт надо добавить конструкцию такого вида:. ./myscript
Предположим, что у нас имеется файл
myfuncs
, который содержит следующее:function addnum {
echo $(( $1 + $2 ))
}
Это — библиотека. Воспользуемся ей в сценарии:
#!/bin/bash
. ./myfuncs
result=$(addnum 10 20)
echo "The result is: $result"
Вызовем его.
Использование библиотек
Только что мы использовали библиотечную функцию внутри скрипта. Всё это замечательно, но что если мы хотим вызвать функцию, объявленную в библиотеке, из командной строки?
Вызов bash-функций из командной строки
Если вы освоили предыдущую часть из этой серии, вы, вероятно, уже догадываетесь, что функцию из библиотеки можно подключить в файле .
bashrc
, используя команду source
. Как результат, вызывать функцию можно будет прямо из командной строки.Отредактируйте
.bashrc
, добавив в него такую строку (путь к файлу библиотеки в вашей системе, естественно, будет другим):. /home/likegeeks/Desktop/myfuncs
Теперь функцию можно вызывать прямо из командной строки:
$ addnum 10 20
Вызов функции из командной строки
Ещё приятнее то, что такая вот библиотека оказывается доступной всем дочерним процессам оболочки, то есть — ей можно пользоваться в bash-скриптах, не заботясь о подключении к ним этой библиотеки.
Тут стоит отметить, что для того, чтобы вышеприведённый пример заработал, может понадобиться выйти из системы, а потом войти снова. Кроме того, обратите внимание на то, что если имя функции из библиотеки совпадёт с именем какой-нибудь стандартной команды, вместо этой команды будет вызываться функция. Поэтому внимательно относитесь к именам функций.
Итоги
Функции в bash-скриптах позволяют оформлять блоки кода и вызывать их в скриптах. А наиболее часто используемые функции стоит выделить в библиотеки, которые можно подключать к скриптам, используя оператор
source
. Если же среди ваших функций найдутся такие, без которых вы прямо таки жить не можете — библиотеки с ними можно подключить в файле .bashrc
. Это позволит удобно пользоваться ими в командной строке или в других скриптах. Главное — чтобы имена ваших функций не совпадали с именами встроенных команд.На сегодня это всё. В следующий раз поговорим об утилите
sed —
мощном средстве обработки строк.Уважаемые читатели! А вы пользуетесь функциями собственной разработки для решения повседневных задач?
Комментарии (33)
CaptainFlint
24.04.2017 19:56Тут стоит отметить, что для того, чтобы вышеприведённый пример заработал, может понадобиться выйти из системы, а потом войти снова.
Если добавляли в .bashrc, то не надо выходить из системы. Достаточно либо запустить новую копию баша (например, открыть новую вкладку в терминале или прямо в текущей вкладке запустить ещё один дочерний bash), либо в текущей командной строке вызвать ту же команду для подгрузки функций, которую добавляли в файл.
Кроме того, обратите внимание на то, что если имя функции из библиотеки совпадёт с именем какой-нибудь стандартной команды, вместо этой команды будет вызываться функция.
И зачастую это очень удобно. Подменяем grep на функцию, которая запускает «настоящий» grep, динамически выбирая для него разные опции в зависимости от того, запущен ли в терминале (а не, скажем, в pipe-цепочке команд):
function grep { if [ -t 1 ] ; then /bin/grep --color -n "$@" else /bin/grep "$@" fi }
iig
24.04.2017 20:16source просто вставляет кусок кода из другого файла. Внутри может и не быть никакой библиотеки.
khim
24.04.2017 22:04+5И опять у нас статья, которая учит пользователей писать неправильные скрипты на bash'е. Давайте немного модифицируем пример с передачей массивов:
#!/bin/bash function myfunc { local newarray newarray=("$@") echo "The new array value is:" for value in "${newarray[@]}" ; do echo " $value" done } myarray=(1 "2 3" "4 5") echo "The original array is:" for value in "${myarray[@]}" ; do echo " $value" done myfunc ${myarray[*]}
Запустим, и…
Таки у нас проблема.The original array is: 1 2 3 4 5 The new array value is: 1 2 3 4 5
А как надо? А вот так:
Теперь — порядок:#!/bin/bash function myfunc { local newarray newarray=("$@") echo "The new array value is:" for value in "${newarray[@]}" ; do echo " $value" done } myarray=(1 "2 3" "4 5") echo "The original array is:" for value in "${myarray[@]}" ; do echo " $value" done myfunc "${myarray[@]}"
Собственно я вообще не могу припомнить ни одного учебника по bash, где не было бы каких-либо ошибок (вThe original array is: 1 2 3 4 5 The new array value is: 1 2 3 4 5
bash
есть много разных способов «выстрелить себе в ногу).
И вот это вот — «простой» язык? Да ни в жизнь. Иначе бы „гуру“, пишущие учебники, уж наверное, не совершали бы „детских“ ошибок.
При этом — всё ведь есть! Давайте например, распечатаем не весь массив в функции, а подмассив:
Работает отлично:#!/bin/bash function myfunc { local newarray newarray=("$@") echo "The new array value is:" for value in "${newarray[@]:1:2}" ; do echo " $value" done } myarray=(1 "2 3" "4 5" 6 7) echo "The original array is:" for value in "${myarray[@]}" ; do echo " $value" done myfunc "${myarray[@]}"
А теперь попробуем „отрезать кусочек“ в момент его создания:The original array is: 1 2 3 4 5 6 7 The new array value is: 2 3 4 5
Всё сьехало:#!/bin/bash function myfunc { local newarray newarray=("${@:1:2}") echo "The new array value is:" for value in "${newarray[@]}" ; do echo " $value" done } myarray=(1 "2 3" "4 5" 6 7) echo "The original array is:" for value in "${myarray[@]}" ; do echo " $value" done myfunc "${myarray[@]}"
Довайте поэкспериментерием:The original array is: 1 2 3 4 5 6 7 The new array value is: 1 2 3
И:#!/bin/bash function myfunc { local newarray newarray=("${@:0}") echo "The new array value is:" for value in "${newarray[@]}" ; do echo " $value" done } myarray=(1 "2 3" "4 5" 6 7) echo "The original array is:" for value in "${myarray[@]}" ; do echo " $value" done myfunc "${myarray[@]}"
Ага. Теперь понятно: $0 — ведь это название скрипта (скрипта, а не функции, заметьте!) и когда мы „массив агументов“ обрабатываем — про это нужно помнить… Но „для удобства“ когда мы работает сThe original array is: 1 2 3 4 5 6 7 The new array value is: test.sh 1 2 3 4 5 6 7
"$@"
нулевой элемент выкидывается автоматически!
Ну что за прелесть этот bash! PHP — это ужас, как всем известно, но bash… bash — это ужас-ужас-ужас-ужас-ужас!RPG
25.04.2017 00:20+3И это вы ещё поверхность не поцарапали...
cd $dir rm -fr *
Спойлер: представим, что $dir не существует или пустой.
file=/etc/passwd foo() { # local file for file in /*; do # file теперь глобально видна всем : done } foo # ой! echo $file
copy_file_to_tmp() { cp "$1" /tmp # работает ровно до момента появления файла с названием "-f" }
Тысячи их.
А ещё
bash -ue script.sh
.
В целом хорошо, что появляются обучающие статьи про баш, плохо то, что учат они не тому. И не учат делать отступы в скриптах.
P.S.
count=$(( $count + 1 )) # так тоже работает ((count++))
P.P.S. В systemd поняли ошибку и переписали всё с шелл-портянок на Си. Но в последний момент всё равно что-то пошло не так...
khim
25.04.2017 01:17+1Ну дык. Решение первой проблемы «всем известно»:
Получаем:#!/bin/bash set -eu dir=oops cd "$dir" rm -rf ./*
Писать скрипты на bash'е без$ bash test.sh test.sh: строка 4: cd: oops: Нет такого файла или каталога
set -eu
— нарываться на неприятности.
Для второй проблемы существует директиваlocal
.
Дляcp
— тоже есть решение, даже два:
1. Можно использоватьcp -- "$1" /tmp
2. Можно сделать функцию примерно такого вида:
После чего имея переменнуюfunction make_safe { if [[ "${1::1}" != "/" && "${1::2}" != "./" ]]; then echo -n "./$1" else echo -n "$1" fi }
unsafe_name
вы можете «легко» сделать её безопасной:
В общем если принять «как данность», что написание скриптов на bash'е — это скорее разгадывание ребусов, то всё можно сделать. Но на нормальное программирование это ни разу не похоже.IFS= read -rd '' safe_name < <(make_safe "$unsafe_name")
Если же скрипты пишутся для себя, то часто можно обойтись более простыми средствами. Если имена файлов, с которыми вам приходится работать не заканчиваются на возврат каретки и не начинаются со знака минус, то ведь и простоcp "$1" /tmp
сработает!iig
25.04.2017 10:45cd $dir rm -fr *
Выстрелить в ногу и жаловаться, что теперь больно.
cd "$dir" && rm -fr *
И тут оказывается, что rm -fr * не удаляет скрытые файлы (имена которых начинаются с точки)…khim
25.04.2017 20:44И тут оказывается, что rm -fr * не удаляет скрытые файлы (имена которых начинаются с точки)…
Я так понимаю, что на это и был рассчёт. Хотя фиг его знает. Целиком скрипта мы не видим, так что…
А вообще —set -eu
надёжнее, чем
Потому что просто тупо игнорировать тот факт, что перейти куда-то не удалось — нехорошо. Нужно либо вывалиться, либоcd "$dir" && rm -fr *
if cd "$dir"
сделать и потом как-то обработать ошибку…iig
25.04.2017 21:14У каждого свой набор шаблонов чтобы подпирать особенности программ на bash ;)
Ошибки желательно обрабатывать, кто ж против.
ghostinushanka
25.04.2017 12:46+3И опять у нас статья, которая учит пользователей писать неправильные скрипты на bash'е.
После того как все мои замечания к предыдущим «статьям» остались без внимания, я просто забил их комментировать.
Было бы неплохо, еслиб НЛО вывесило баннер над этими статьями в стиле «Бородатые комментаторы советуют к статьям относиться со скепсисом»Termux
25.04.2017 15:58Бородатые комментаторы весь баш выкинули уже 3 года как
и увольняют за попытку его использования
Ansible проще поддерживать и развиватьneumeika
26.04.2017 00:19ну-ну, если б они пореже функционал меняли. Обновился, а переменные уже локальные для include и переопределить их можно только через set_fact. Или with_item синтаксис поменяли, или loop_var. Я уже заколебался переписывать. В баше такого нет :)
cccco
Спасибо!
cccco
Автор написал свой пост в 16:10. Через полтора часа никакой реакции от сообщества не было. Я решил поддержать автора и написал ему слова благодарности. Тем более нашёл в его публикации что-то полезное для себя.
Не думал, что слова благодарности автору поста, могут вызвать у кого-то неприязнь. Тем более, у меня читающий аккаунт, а значит, мой комментарий прошёл модерацию автора, т.е. он его одобрил.
Мне кажется, минусовать без пояснения, в данном случае, — некрасиво, это — обнажать какие-то свои скрытые комплексы.
grossws
Вы читали комментарии к прошлым "статьям" (точнее переводам)? Я читал. Включая комментарии.
Вы считаете, что необходимо поддерживать серию объективно плохих статей, в которых редакторы корпоративного блога уделяют сообществу, указывающему на вопиющие ошибки в них ~ ноль внимания? Я не считаю.
Эти статьи учат плохому и кто-нибудь, "удачно" воспользовавшись этими статьями, рано или поздно положит что-нибудь на проде/оставит хорошую уязвимость и т. п. И эти проблемы уже будут решать люди, отметившиеся в комментариях разумной критикой, а не авторы корпоративного блога и восторженные почитатели серии статей.
cccco
Кроме лирики конкретика какая-нибудь будет?
Я, например, не считаю ЭТУ статью плохой. И по комментариям к ЭТОЙ статье я это тоже вижу. Идёт здоровая дискуссия, аналогичная дискуссиям в комментариях к другим статьях на Хабре.
Я не знаю, может у Вас что-то этакое есть, позволяющие Вам безапелляционно навешивать всякие ярлыки («Тут всё плохо!» и далее «Я всех спасу!»). Вы, уж, предъявите тогда это, чтобы всем было понятно, что Вы за фрукт. Какую-нибудь ссылку на свою шикарную статью по данной тематике, например, или, может быть, даже книгу.
Пока что о Вас создаётся впечатление какого-то обиженного мстителя. Отметились бы для начала разумной критикой, что ли.
Как-то неубедительно Вы звучите пока.
grossws
Если говорить об этой статье: неправильная работа с массивами, использование
.
в скриптах. Вполне достаточно.Плюс, эти проблемы уже освещались в комментариях к этой статье. Например, пользователем khim.
cccco
Не согласен, абсолютно. Доводы — никакие совсем.
Во-первых, как-то малова-то т.н. «недочётов» для такой обширной статьи, чтобы называть её плохой.
Во-вторых, про массивы. В том контексте, в котором упоминается работа с массивами, я, например, не вижу ничего неправильного. Скрипт отработает так, как нужно.
Да, khim указал, как более правильно использовать массивы, учитывающие разные контексты массивов. За это ему также огромное спасибо! Но формально-то автор прав. В этом и есть прелесть любой хорошей статьи на Хабре, что она не оставляет равнодушными тех, кто в комментариях эту статью может улучшить. Частенько, в комментариях к статьям содержится не меньше полезной информации, чем в самих статьях.
Я, например, хотел бы, чтобы эта дискуссия про Bash на Хабре продолжилась. Поэтому, спасибо автору и комментаторам, что они тратят своё личное время на это!
В-третьих, про точку в скриптах. Я не понял, что тут неправильного? Поясните, пожалуйста. Если открыть наугад, идущий в стандартной установки Linux, скрипт в /etc/init.d или /etc/rc.d (или что-то ещё похожее, не знаю, с чем Вы работаете), наверняка Вы там встретите использование точки. Я точку встречаю во многих скриптах.
Кроме того, автор не призывает использовать или не использовать точку в скриптах. Автор поделился вполне конкретной информацией о том, что source имеет псевдоним — точку. Разве это неправильная информация?
grossws
В некоторых частных случаях. А учить писать на баше так, что обычно работает, но может сломаться в самый удачный момент, на мой взгляд, в корне неверно. Баш — минное поле и очень неприятное legacy.
Часть комментаторов уже сдалась, т. к. в этой серии ошибки кочуют из статьи в статью.
Использование точки, равно как и коротких опций в скриптах — code smell. Это ухудшает поддерживаемость скрипта. Насчёт же init.d/rc.d на моей системе оно выглядит так:
На centos7 похуже, там больше всякого legacy-барахла. Вообще поддерживать старые добрые init-скрипты на баше даже под пару версий дистрибутива уже не очень приятно. А уж ориентироваться на них, как на образец написания программ на баше так вообще не стоит. Говнокода там хватает.
Я понял, что вам понравилась эта серия статей. Не буду мешать вам оставаться при своём мнении. Предлагаю завершить дискуссию.
ghostinushanka
Вот только
нихрэто не верное утверждение. Если сильно притянуть за уши — то это sourceпсевдонимсиноним «точки», поскольку точка перекочевала из оригинального борн шела а source — нововведение (причём я не рискну сказать баша или ksh).Но если посмотреть мануал, а ещё лучше вообще исходники 23:52, то выяснится что это две абсолютно равнозначных встроенных команды (built-in) вызывающих одну и ту же функцию source_builtin
А вы вообще знаете почему вы встречаете точку-то? Нет? А эта «статья» почему-то этот весьма важный момент опустила, не смотря на то, что об этих встроенных командах обмолвилась.
В качестве повышения квалификации предлагаю вам эту информацию нарыть и поделиться с другими читателями. Ключевое слово — POSIX. В догонку можете поискать на что ресолвится /bin/sh в современных линуксовых дистрах (для упрощения давайте возьмём debian, ubuntu, centos, opensuse, arch), и что будет если скрипт с шебангом #!/bin/sh этому делу скормить и в чём будет разница между source и точкой.
И о такого рода недочётах, которые рано или поздно начинающих приведут к «выстрели себе в ногу рикошетом в голову», я могу к каждой из этих публикаций писать полотна, но, зачем?! Ниже iig написал про absg. Этот материал я упоминал в комментариях к предыдущим статьям как минимум трижды.
Мой добрый совет — читайте мной ранее упомянутые вещи, а не это.
cccco
Спасибо огромное за совет! У меня он тоже есть для Вас.
Учитесь слышать людей.
Ваш опус в контексте статьи — ненужная шелуха, информация, чтобы блеснуть умом перед девочками. К чему Вы его написали-то? (Подумайте над этим вопросом и дайте себе ответ, только правдивый.)
Он доказывает, что точку в скриптах использовать неправильно? Нет. Мимо.
Он как-то показывает, что, если я буду использовать точку вместо source, я получу другой результат? Опять нет. «Молоко».
Автор статьи всего лишь пытался донести, что вместо source с таким же успехом можно использовать точку. Не больше, не меньше. А Вы эту простую мысль-то и не услышали! (И давай обнажать свой «ум».)
А так да, Вы написали много правильных слов. Но, как-то не туда, понимаете. Мимо. Может Вам книгу лучше написать, с таким умищем-то? И девочкам это нравится ещё больше.
Короче, перечитывайте этот мой комментарий много раз. Он вам поможет научиться слышать людей. Это Вам мой добрый совет.
grossws
Хамство вас не красит, успокойтесь
cccco
Успокойтесь, пожалуйста! Нам с ghostinushanka суфлёр не нужен. Мы сами разберёмся.
Кроме того нашу с Вами дискуссию мы уже прекратили (кстати, по Вашей же инициативе).
Не тратьте, пожалуйста, своё личное время на меня. Я своё точно уже на Вас тратить не собираюсь. Если хотите как-то отреагировать, то просто продолжайте меня минусовать, это точно не отнимет у Вас много Вашего личного времени.
Услышьте меня, пожалуйста, наконец!
ghostinushanka
Иными словами вы не стали ничего узнавать про POSIX.
В контекте вот этой вашей фразы, это, к великому моему сожалению, весьма печально:
Да получите, причём в самый неожиданный момент. Или вы серьёзно считаете, что я со злым умыслом и от нечего делать вас направил что-то поискать почитать?
Вместо повышения уровня своих знаний вы написали совершенно неуместный ответ с переходом на личности. Ваш выбор, ровно как и выбор быть довольным этим циклом не смотря на замечания к нему.
#irony #rhetorical
А где мне спасибо на прямые ссылки на «Advanced bash scripting guide», которые я приводил комментариями к предыдущим статьям?
P.S. Прошу прощения у хабрапублики. Я надеюсь что эти комментарии всё же помогут начинающим и не очень писателям шелл скриптов.
Подписываюсь под этим абсолютно верным комментарием к следующей «статье из цикла».
cccco
Очень жаль, что Вы даже и не попробовали научиться слышать людей!
Вы уж расскажите, пожалуйста, не томите.
Вы обиделись, а зря! Вы что, серьёзно считаете, что я со злым умыслом и от нечего делать Вас попытался направить научиться хоть немного слышать людей? Отнюдь.
Если бы Вы меня услышали, Вы бы увидели, что мой комментарий, который Вас почему-то обидел, был по форме зеркальной копией Вашего комментария. Другими словами, Вы по сути обиделись сами на себя. Согласитесь, это хорошая информация для размышления.
Вместо того, чтобы улучшить себя, Вы опять написали совершенно неуместный смешной ответ в назидательном тоне. Любят учить, Вы сами знаете кто.
Вот, Вы всё зачем-то вставляете в своих комментариях «POSIX, POSIX...». Поймите Вы, наконец, что очки — это ещё не признак ума. А, вот, умение слышать собеседника, по крайней мере, даёт надежду на его наличие.
Конечно, это — Ваш выбор даже не пытаться научиться слышать людей, ровно как и выбор любоваться собой, никого не слыша, но, скажу Вам, что никто не хочет тратить своё личное время на собеседника, неумеющего слышать.
К великой моей Радости, я бы очень хотел, чтобы Вы научились слышать людей. Но я не могу Вас заставить! Вы должны сами этого захотеть. Иначе — не получится. Я лишь могу только выдавать Вам информацию для размышления.
Например.
Разве Вы слышали от меня, что я ничего не знаю про POSIX? Как я могу, например, писать многопоточные сервера и не знать про POSIX? Я POSIX уже хорошо знал ещё в 90-х. Но я не вижу в этом ничего экстраординарного — знать POSIX. С таким же успехом Вы могли бы мне в также назидательном тоне посоветовать изучать, например, Linux. Моё восприятие Ваших слов совершенно бы не изменилось. Ваш совет меня искренне улыбнул. :)
Я хорошо понимаю, почему Вы по-доброму советуете мне, и с великим сожалением расстраиваетесь, что по-вашему я это не делаю, изучать именно POSIX, а, например, не Linux (в контексте Ваших неуместных обращениях ко мне я разницы не вижу). Просто для Вас он ещё свеж, а Linux скорее всего уже нет или лучше так — не так свеж. Поразмышляйте над этим.
Ещё, например.
Я совершенно не сомневался, что Вы обязательно напишите мне что-то типа этого:
Вы умудрились обидеться (хотя и прикрылись якобы иронией) на комментарий, который предназначался не для Вас. Я, конечно, оброщался к iig, но для Вас этот комментарий тоже предназначался, но, только в качестве эксперимента. Мне же надо было как-то получить от Вас косвенные подтверждения, Вашей обидчивости. Спасибо, тест сдан, Вы это подтвердили! Поразмышляйте над этим тоже.
Продолжим.
Ответьте честно для себя, как наша дискуссия поможет кому-то писать хорошие шелл скрипты? Вы же ничего конкретного по теме не написали вообще, в отличие от, например, khim. Откровенные самолюбование, желание «примазаться к славе», и, конечно, неуверенность в себе. Поразмышляйте и над этим.
Тут на Хабре есть реальные ребята, делящиеся своими знаниями в статьях и комментариях и делающих Хабр тем, что он есть, а есть закомплексованные критики, как правило, без публикаций и без конкретики в комментариях — одна лишь пустая неподкреплённая ничем критика (именно они активно минусуют любого, потому что никого не слышат) и лирика (активно стремяться всем «помочь», при этом ничем не помогая, и «спасти» — не спасая).
Хотите перейти на сторону реальных ребят? — научитесь для начала слышать людей.
Лимит моего личного времени на дискуссию с Вами, к сожалению, закончился. Но Вы, если у Вас есть что ответить, обязательно ответьте, а я обязательно прочту. Я не обидчивый. Мне людей изучать тоже интересно.
iig
Канонический Advanced bash scripting guide не намного больше по размеру, я бы его рекомендовал.
cccco
Спасибо, добрый человек!
Хотя за слова благодарности тут есть много минусующих, я всё-таки решил поблагодарить за реально полезную информацию. Кто-нибудь, случайно наткнувшись на эту статью, сможет легко двинуться дальше (как и я, например).
malinichev
Без обид, но если уж на то пошло — соберитесь вместе с умными людьми, и напишите свою серию статей!
И 100% найдётся люди умнее и также будут комментировать…
Вот честно, вы так высказываетесь, как будто тут выложили совершенно не работающий код. Главное — что он работает, а подточить — уже дело каждого.
Я, например, пол года назад совершенно не знал об администрировании серверов, а сейчас приходится содержать 2 сервера, и для меня хоть какие-то примеры — это послание от бога! Но я согласен, что примеры не ахти моментами, но что поделать…
grossws
Не беспокойтесь, без ваших мудрых советов от активного автора разберусь чему посвятить своё свободное время. И тут мои и не только мои open source проекты куда в большем приоритете, чем статьи на хабре.