Доступ к репозиторию по сети

Перед переходом к особенностям написания bash-сценариев следует изучить методику этого процесса. Многие администраторы пишут bash-сценарии так же, как Perl- или Python-сценарии, т.е. используя какой-либо текстовый редактор, но рекомендуется для удобства рассматривать в качестве интерактивной среды разработки сценариев режим с приглашением на ввод команды.

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

Сначала нужно отыскать все эти файлы:

$ find. -name '*log'
.do-not-touch/important.log
admin.com-log/
foo.log
genius/spew.log
leather_flog

Далее необходимо включить в шаблон точку и игнорировать при поиске каталоги, для чего нажать комбинацию клавиш Ctrl+P, чтобы вернуть команду в командную строку, а затем модифицировать ее.

$ find.-type f -name '*.log'
.do-not-touch/important.log
foo.log
genius/spew.log

С внесением таких изменений вывод команды становится более понятным. При необходимости можно избавиться от каталога .do-not-touch следующим образом:

$ find. -type f -name '*.log ' | grep -v.do-not-touch
foo.log
genius/spew.log

Теперь получен абсолютно точный список файлов, которые должны быть переименованы.

Далее можно попробовать сгенерировать несколько новых имен.

$ find. -type f -name '*.log ' I grep -v.do-not-touch | while read fname
> do
> echo mv $ fname $ {fname/. log/. LOG/}
> done
mv foo.log foo.LOG
mv genius/spew.log genius/spew.LOG

Эти команды позволяют переименовать нужные файлы.

В реальности можно снова вызвать уже выполненную команду и отредактировать команду echo, чтобы заставить bash-оболочку выполнять команды mv, а не просто выводить их, так как передача команд в отдельную копию оболочки bash более надежный вариант работы, который к тому же требует меньшего объема редактирования.

При нажатии комбинации клавиш Ctrl+P bash-оболочка сворачивает мини-сценарий данного примера в одну. К этой уплотненной командной строке нужно добавить канал, передающий выходные данные команде bash -х.

$ find. -type f -name '*.log ' I grep -v.do-not-touch | while read fname;
do echo mv $fname $ {fname/. log/. LOG/}; done | bash -x
+ mv foo.log foo.LOG
+ mv genius/spew.log genius/spew.LOG

Ключ -x команды bash обеспечивает вывод каждой команды перед ее выполнением.

Теперь переименование файлов завершено, но полезно сохранить этот сценарий, чтобы воспользоваться им вновь. Встроенная в bash команда fc по своему действию во многом аналогична нажатию комбинации клавиш Ctrl+P, но вместо возврата последней команды в командную строку она передает команду в заданный редактор. Следует добавить в нужный файл строку идентификационного комментария, поместить сохраненный файл в приемлемый каталог (например, ~/bin или /usr/local/bin), сделать файл исполняемым, чтобы в итоге получить настоящий сценарий.

Рекомендации по использованию сценариев:

  • Разработка сценария (или его части) должна быть в виде конвейера команд, причем пошагово и в режиме выполнения командных строк;
  • Пересылка результата должна осуществляться в стандартный выходной поток, проверяя правильность работы используемых команд;
  • Использовать на каждом этапе буфера ранее выполненных команд для их появления в командной строке и инструментов редактирования для их модификации;
  • Проводить сохранение всех команд сценария до тех пор, пока команды не заработают должным образом;
  • Проводить выполнение команды на реальном примере при получении правильного результата, чтоб убедиться в достоверности;
  • Использование команды fс для фиксации работы в редакторе, оформления ее соответствующим образом и сохранения. В приведенном выше примере были выведены командные строки, а затем направлены в подоболочку для выполнения. Этот метод не является универсально применимым, но часто оказывается полезным. В качестве альтернативного варианта можно фиксировать результаты, перенаправляя их в файл. Рекомендуется добиваться при разработке сценариев получения нужных результатов, и до этих пор не выполнять никаких потенциально деструктивных действий.

Доступ по HTTP

Команда echo не отличается интеллектуальностью, но зато проста в применении. Для получения большего контроля над выводом данных используют команду printf, которая менее удобна, поскольку предполагает указание в нужных местах символов перехода на новую строку \n, но при этом позволяет использовать символ табуляции и другие средства форматирования результата.

Для сравнения результаты выполнения следующих двух команд:

$ echo "\taa\tbb\tcc\n"
\taa\tbb\tcc\n
$ printf "\taa\tbb\tcc\n"
аа bb сс

Для того чтобы сформировать пользователю приглашение ввести данные, можно использовать команду read:

#!/bin/bash
echo -n "Ввести свое имя: "
read user_name
if [ -n "$user_name" ]; then
echo "Привет $user_name!"
exit 0
else
echo "Вы не назвали свое имя!"
exit 1
fi

Доступ по NFS

Аргументы командной строки служат для сценария переменными с числовыми именами: $1 – первый аргумент командной строки, $2 – второй и т.д. Аргумент $0 содержит имя, по которому был вызван сценарий, например, ./bin/example.sh, т.е. это не фиксированное значение.

Переменная $# содержит количество переданных аргументов командной строки, а переменная $* все эти аргументы. Ни одна из этих переменных не учитывает аргумент $0.

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

#!/bin/bash
function show_usage {
echo "Использование: $0 source_dir dest_dir"
exit 1
}
Основная программа начинается здесь
if [ $# -ne 2 ]; then
show_usage
else # Существуют два аргумента
if [ -d $1 ]; then
source_dir=$l
else
echo 'Недопустимый каталог-источник'
show_usage
fi
if [ -d $2 ]; then
dest_dir=$2
else
echo 'Недопустимый каталог-приемник'
show_usage
fi
fi
printf "Каталог-источник: ${source_dir}\n"
printf "Каталог-приемник: ${dest_dir}\n"

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

$ mkdir ааа bbb
$ sh showusage ааа bbb
Каталог-источник: ааа
Каталог-приемник: bbb
$ sh showusage foo bar
Недопустимый каталог-источник
Использование: showusage source__dir dest_dir

Аргументы bash-функций обрабатываются практически так же, как и аргументы командной строки: $1 – первый аргумент командной строки, $2 – второй и т.д. Как видно из приведенного выше примера, аргумент $0 содержит имя сценария.

Для того чтобы сделать предыдущий пример более устойчивым к ошибкам, можно передавать функции show_usage в качестве аргумента код ошибки. Это позволило бы конкретизировать код возврата для каждого типа отказа. Реализация этой идеи показана в следующем фрагменте программы:

function show_usage {
echo "Использование: $0 source_dir dest_dir"
if [ $# -eq 0 ]; then
exit 99 # Выход с любым ненулевым кодом возврата
else
exit $1
fi
}

В этой версии функции добавляется анализ наличия аргументов. Внутри функции переменная $# сообщает, сколько аргументов было ей передано. Сценарий завершится с кодом возврата 99, если при его вызове не было передано никаких аргументов. Но при передаче конкретного значения, например, "show_usage 5", сценарий завершится с этим кодом после вывода сообщения об использовании сценария (переменная $? содержит статус завершения последней выполненной команды, независимо от факта использования в сценарии или в командной строке).

Между функциями и командами в bash-оболочке существует строгая аналогия. Можно определить полезные функции в файле ~/.bash_profile, а затем использовать их в командной строке как команды. Например, если узел стандартизировал в сети порт 7988 для протокола SSH, можно определить в файле ~/.bash_profile функцию ssh, чтобы она всегда запускалась (как команда) с ключом -р 7988.

function ssh {
/usr/bin/ssh 7988 $*
}

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