Использование позиционных параметров в функциях
Позиционные параметры используются для передачи аргументов не только в сценарии, но и в функции командной оболочки. Для демонстрации преобразуем сценарий file_info в функцию:
file_info () {
# file_info: функция для вывода информации о файле
if [[ -e $1 ]]; then
echo -e "nFile Type:"
file $1
echo -e "nFile Status:"
stat $1
else
echo "$FUNCNAME: usage: $FUNCNAME file" >&2
return 1
fi
}
Теперь, если сценарий, включающий функцию file_info, вызовет ее с именем файла в аргументе, аргумент будет передан в функцию.
Благодаря этому мы получаем возможность написать множество полезных функций для использования не только в наших сценариях, но и в файле .bashrc.
Обратите внимание, что в этом примере вместо переменной PROGNAME используется переменная командной оболочки FUNCNAME. Оболочка автоматически присваивает значение этой переменной в момент вызова функции. Отметьте также, что $0 всегда содержит полный путь к первому элементу командной строки (то есть имя программы), а не имя функции, как можно было бы ожидать.
Обработка позиционных параметров скопом
Иногда бывает необходимо выполнить операцию сразу со всеми позиционными параметрами. Например, может понадобиться написать обертку для некоторой программы, то есть сценарий или функцию, упрощающие запуск этой программы. Обертка принимает список непонятных для нее параметров командной строки и просто передает его обернутой программе.
Для этой цели командная оболочка предоставляет два специальных параметра. Они оба замещаются полным списком позиционных параметров, но имеют некоторые тонкие отличия. Описание этих параметров приводится в табл. 32.1.
Таблица 32.1. Специальные параметры $* и [email protected]
Параметр
Описание
$*
Замещается списком позиционных параметров, начиная с $1. Если имя параметра $* заключить в двойные кавычки, позиционные параметры будут перечислены в списке через первый символ в переменной IFS (по умолчанию пробел), а сам список будет размещен в одной строке и заключен в кавычки
[email protected]
Замещается списком позиционных параметров, начиная с $1. Если имя параметра [email protected] заключить в двойные кавычки, механизм подстановки заменит его списком позиционных параметров, заключенных в кавычки по отдельности
Следующий сценарий демонстрирует, как действуют эти специальные параметры:
#!/bin/bash
# posit-params3 : сценарий для демонстрации $* и [email protected]
print_params () {
echo "$1 = $1"
echo "$2 = $2"
echo "$3 = $3"
echo "$4 = $4"
}
pass_params () {
echo -e "n" '$* :'; print_params $*
echo -e "n" '"$*" :'; print_params "$*"
echo -e "n" '[email protected] :'; print_params [email protected]
echo -e "n" '"[email protected]" :'; print_params "[email protected]"
}
pass_params "word" "words with spaces"
В этой довольно замысловатой программе мы создали два аргумента, word и words with spaces, и передали их функции pass_params. Эта функция, в свою очередь, передает их функции print_params, с применением каждого из четырех методов, доступных для специальных параметров $* и [email protected] Вывод сценария показывает разницу между ними:
[[email protected] ~]$ posit-param3
$* :
$1 = word
$2 = words
$3 = with
$4 = spaces
"$*" :
$1 = word words with spaces
$2 =
$3 =
$4 =
[email protected] :
$1 = word
$2 = words
$3 = with
$4 = spaces
"[email protected]" :
$1 = word
$2 = words with spaces
$3 =
$4 =
В данном примере оба параметра, $* и [email protected], возвращают результат из четырех слов: word, words, with и spaces. "$*" возвращает результат в виде одного слова, содержащего пробелы: word words with spaces. "[email protected]" возвращает результат в виде двух слов, второе из которых включает пробелы: word и words with spaces.
Это соответствует нашим фактическим намерениям. Этот пример показывает, что, несмотря на наличие четырех разных способов получения списка позиционных параметров, в большинстве ситуаций предпочтительнее использовать прием с "[email protected]", потому что он сохраняет целостность каждого позиционного параметра.
Более сложное приложение
После долгой паузы мы продолжим работу над программой sys_info_page. Теперь мы добавим в нее поддержку нескольких параметров командной строки:
• Выходной файл. Мы добавим параметр, который позволит указать имя файла для вывода результатов работы программы. Сделать это можно будет с помощью -f файл или --file файл.
• Интерактивный режим. При передаче этого параметра программа будет предлагать пользователю ввести имя выходного файла и определять, существует ли этот файл. Если файл существует, пользователю будет предложено подтвердить свое решение, прежде чем затереть существующий файл. Этот параметр можно будет передать как -i или --interactive.
• Справка. Передав параметр -h или --help, можно потребовать от программы вывести сообщение с информацией о правилах пользования программой.
Далее приводится код, реализующий обработку командной строки:
usage () {
echo "$PROGNAME: usage: $PROGNAME [-f file | -i]"
return
}
# обработка параметров командной строки
interactive=
filename=
while [[ -n $1 ]]; do
case $1 in
-f | --file) shift
filename=$1
;;
-i | --interactive) interactive=1
;;
-h | --help) usage
exit
;;
*) usage >&2
exit 1
;;
esac
shift
done
Сначала мы добавили функцию usage для вывода сообщения, если программа вызывается с параметром --help или с неизвестным параметром.
Затем следует цикл обработки параметров. Цикл продолжается, пока позиционный параметр $1 не получит пустое значение. В конце цикла вызывается команда shift, чтобы сдвинуть позиционные параметры и, в конечном итоге, гарантировать завершение цикла.
Внутри цикла инструкция case проверяет текущий позиционный параметр на соответствие поддерживаемым вариантам. Если данный параметр поддерживается, выполняется соответствующая операция, если нет — выводится сообщение с информацией о правилах пользования программой и сценарий завершается с признаком ошибки.
Обратите внимание, как обрабатывается параметр -f. Обнаружив этот параметр, программа выполняет команду shift, которая сдвинет аргумент параметра -f с именем файла в позиционный параметр $1.
Далее следует код, реализующий интерактивный режим:
# интерактивный режим
if [[ -n $interactive ]]; then
while true; do
read -p "Enter name of output file: " filename
if [[ -e $filename ]]; then
read -p "'$filename' exists. Overwrite? [y/n/q] > "
case $REPLY in
Y|y) break