355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Дэвид Тейнсли » Linux и UNIX: программирование в shell. Руководство разработчика » Текст книги (страница 15)
Linux и UNIX: программирование в shell. Руководство разработчика
  • Текст добавлен: 15 октября 2016, 00:39

Текст книги "Linux и UNIX: программирование в shell. Руководство разработчика"


Автор книги: Дэвид Тейнсли



сообщить о нарушении

Текущая страница: 15 (всего у книги 25 страниц)

LOOK_OUT=`df | grep /logs | awk '{print $5}' | sed 's/%//g'`

echo $LOOK_OUT

until [ "$LOOK_OUT" -gt "90" ]

do

echo "Filesystem..logs ls nearly full" | mail root

done

exit 0

18.7. Цикл while

Цикл while выполняет ряд команд до тех пор, пока истинно условие. Этот цикл используется также для просмотра данных из файла ввода. Формат цикла while:

while команда

do

команды1

команды2

done

Между конструкциями while и do находится несколько команд, хотя в общем случае применяется только одна команда. Обычно команда выполняет проверку условия.

Команды, размещенные между ключевыми словами do и done, выполняются только в том случае, если код завершения command равен нулю; если код завершения принимает какое‑либо другое значение, цикл заканчивается.

Когда команды выполнены, контроль передается обратно, в верхнюю часть цикла. И все начинается снова до тех пор, пока проверяемое условие отлично от нуля.

18.7.1. Простой цикл while

Ниже приводится основная форма цикла while. Условие тестирования состоит в том, что если "COUNTER is less than 5", условие останется истинным. Переменная counter имеет начальное значение нуль, и ее значение увеличивается на постоянную величину при выполнении цикла.

$ pg whilecount

#!/bin/sh # whilecount COUNTER=0

#счетчик равен 5?

while [ $COUNTER -lt 5 ] do

#прибавление к счетчику единицы

COUNTER=`expr $COUNTER + 1`

echo $COUNTER

done

Указанный сценарий выводит на экран числа от 1 до 5, затем завершает работу.

$ whilecount

1 2 3

4

5

18.7.2. Применение цикла while при вводе с клавиатуры

Цикл while может применяться для ввода информации с клавиатуры. В следующем примере введенная информация присваивается переменной film. Если нажать клавиши [Ctrl+D], цикл завершает выполнение.

$ pg whileread

#!/bin/sh

# whileread

echo " type to terminate"

echo -n "enter your most liked film :"

while read FILM

do

echo "Yeah, great film the $FILM"

done

Когда сценарий выполняется, вводимыми данными могут быть:

$ whileread

enter your most liked film: Sound of Music

Yeah, great film the Sound of Music

18.7.3. Применения цикла while для считывания данных из файлов

Обычно цикл while используется для считывания данных из файла, что позволяет сценарию обрабатывать информацию.

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

$ pg names.txt

Louise Conrad:Accounts:ACC8987

Peter James:Payroll:PR489

Fred Terms:Customer:CUS012

James Lenod:Accounts:ACC887

Frank Pavely:Payroll:PR489

Для хранения строк данных можно использовать переменные. Условие истинно до тех пор, пока не считываются новые данные. Для просмотра содержимого файла цикл while использует перенаправление потока данных ввода. Обратите внимание, что отдельной переменной $LINE присваивается целая строка.

$ pg whileread

#!/bin/sh

# whileread

while read LINE

do

echo $LINE

done < names.txt

$ whileread

Louise Conrad:Accounts:ACC8987

Peter James:Payroll:PR489

Fred Terras:Customer:CUS012

James Lenod:Accounts:ACC887

Frank Pavelу:Payroll:PR4 8 9

18.7.4. Считывание данных из файлов с помощью IFS

Чтобы при выводе данных устранить разделитель полей в виде двоеточия, примените переменную ifs, предварительно сохранив ее установки. После того как сценарий завершит работу с этими установками, восстановите установки переменной ifs. С помощью переменной ifs можно изменить разделитель полей на двоеточие вместо пробела или символа табуляции, которые заданы по умолчанию. Как известно, отдельной переменной можно присвоить значения трех полей: NAME, dept и id.

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

$ pg whilereadifs

#!/bin/sh

#whilereadifs

#сохраните установку IFS

SAVEDIFS=$IFS

#присвоим переменной IFS новый разделитель

IFS=:

while read NAME DEPT ID

do

echo -e "$NAMEt $DEPTt $ID" done < names.txt

# восстановим установки переменнойIFS

IFS=$SAVEDIFS

При выполнении сценария получим более привлекательный поток вывода:

$ whilereadifs

Louise Conrad Accounts ACC8987

Peter James Payroll PR489

Fred Terms Customer CUS012

James Lenod Accounts ACC887

Frank Pavely Payroll PR489

18.7.5. Обработка файла с помощью проверок условий

Большинство циклов while включает некоторый оператор проверки, который уточняет последовательность действий.

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

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

сократить количество типов. Все редакторские правки находятся в начале, а не рассеяны по всему сценарию.

$ pg whileread_file

#!/bin/sh

#whileread_file

#инициализация переменных

SAVEDIFS=$IFS

IFS=:

HOLD_FILE=hold_file

NAME_MATCH="James Lenod"

INPUT_FILE=names.txt

# создавайте каждый раз новый HOLD_FILE, в случае, когда сценарий

непрерывно выполняется

>$HOLD FILE

while read NAME DEPT ID

do

#выводит на экран всю информацию в holdfile с помощью перенаправления

echo $NAME $DEPT $ID >>$HOLD_FILE

#имеется ли соответствие ???

if [ "$NAME"="$NAME_MATCH" ]; then

# да, тогда удобно завершить работу

echo "all entries up to and including $NAME_MATCH are in $HOLD_FILE"

exit 0

fi

done < $INPUT_FILE

# восстановление IFS

IFS=$SAVEDIFS

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

$ pg whileread_cond

#!/bin/sh

# whileread_cond

# инициализация переменных ACC_LOOP=0; CUS_LOOP=0; PAY_LOOP=0;

SAVEDIFS=$IFS

IFS=:

while read NAME DEPT ID

do

# счетчик увеличивается на единицу для каждого совпадающего названия отдела.

case $DEPT in

Accounts)

ACC_LOOP=`expr $ACC_LOOP + 1`

ACC="Accounts"

;;

Customer)

CUS_LOOP=`expr SCUS_LOOP + 1`

CUS="Customer"

;;

Payroll)

PAY_LOOP=`expr $PAY_LOOP + 1`

PAY="Pay roll"

;;

*) echo "`basename $0`: Unknown department $DEPT" >&2

;;

esac

done < names.txt

IFS=$SAVEDIFS

echo "there are $ACC_ LOOP employees assigned to $ACC dept"

echo "there are $CUS_LOOP employees assigned to $CUS dept"

echo "there are $PAY_LOOP employees assigned to $PAY dept"

При выполнении сценария получим следующий вывод:

$ whileread_cond

there are 2 employees assigned to Accounts dept

there are 1 employees assigned to Customer dept

there are 2 employees assigned to Payroll dept

18.7.6. Выполнение суммирования

Довольно часто приходится сталкиваться с задачей считывания информации из файла и выполнения суммирования по определенным столбцам, содержащим числа. Предположим, в файле total.txt находятся данные о продажах отделами stat и gift..

$ pg total.txt


STAT

3444

GIFT

233

GIFT

252

GIFT

932

STAT

212

STAT

923

GIFT

129

Задача состоит в подсчете общей суммы всех записей отдела gift. Чтобы сохранить общие значения сумм, применим оператор expr. Как показано в следующем операторе expr, переменным loop и total первоначально вне цикла присваивается значение ноль. Когда сценарий выполняет цикл, значение переменной items добавляется к значению переменной total. В первую итерацию цикла входит только первый пункт, но в дальнейшем к накапливающимся значениям переменной total добавляются значения переменной items.

Следующий оператор expr увеличивает значение счетчика.

LOOP=0

TOTAL=0

while…

TOTAL=`expr $TOTAL + $ITEMS`

ITEMS=`expr $ITEMS + 1`

done

Очень распространенной является такая ошибка: при работе с оператором expr забывают сначала инициализировать переменную.

LOOP=0

TOTAL=0

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

TOTAL=`expr ${TOTAL:=0} + ${ITEMS}`

В вышеприведенном примере переменной total присваивается значение нуль, если эта переменная не имеет значения. Чаще распространен первый вариант инициализации переменных с помощью оператора expr. Не забывайте выводить на экран конечное общее значение, полученное в результате выполнения цикла.

Рассмотрим следующий сценарий.

$ pg total

#!/bin/sh

#общая сумма

#инициализация переменных

LOOP=0

TOTAL=0

COUNT=0

echo "items Dept"

echo " "

while read DEPT ITEMS do

# сохраните результаты подсчета при просмотре общих записей

COUNT=`expr $COUNT + 1`

if [ "$DEPT"="GIFT" ]; then

# сохраните выполнение суммирования TOTAL=`expr $TOTAL + $ITEMS` ITEMS=`expr $ITEMS + 1`

echo -e "$ITEMSt$DEPT"

fi

#echo $DEPT $ITEMS done < total.txt

echo $TOTAL

echo "There were $COUNT entries altogether in the file"

При выполнении сценария получим:

$ total

Items

Dept

234

GIFT

253

GIFT

933

GIFT

130

GIFT

======

======

1546

There were 7 entries altogether in the file

18.7.7. Одновременный просмотр двух записей

В некоторых случаях возникает необходимость в одновременной обработке двух записей, например, если нужно сравнить различные поля двух записей. Чтобы просматривать одновременно по две записи, достаточно после первой конструкции "while read" поместить оператор "do" с другим оператором "read". Применяя эту методику, следует помнить, что количество записей для просмотра составляет четное число. Не забывайте проверять число просматриваемых записей!

Ниже приводится файл, содержащий шесть записей: record1, record2 и т. д.

$ pg record.txt

record 1

record 2

record 3

record 4

record 5

record 6

В этом примере одновременно просматривается по две записи. Никакой проверки записей в примере не производится.

Ниже приводится соответствующий сценарий.

$ pg readpair

#!/bin/sh

#readpair

#первая запись

while read rec1 do

#вторая запись

read rec2

#выполняется дальнейшая обработка/тестирование для тестирования или

#сравнения обеих записей

echo "This ls record one of a pair :$rec1"

echo "This ls record two of a pair :$rec2"

done < record.txt

С помощью команды wc сначала проверим, что было выполнено четное число записей.

$ cat record.txt | wc -l

б

Имеется шесть записей, поэтому приступим к их обработке.

$ readpair

This ls record one of a pair :record 1

This ls record two of a pair :record 2

This ls record one of a pair :record 3

This ls record two of a pair :record 4

This ls record one of a pair :record 5

This ls record two of a pair :record 6

18.7.8. Игнорирование символа #

При просмотре текстовых файлов возникает необходимость игнорировать или пропускать строки комментария. Ниже приводится типичный пример.

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

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

Ниже приводится типичный файл конфигурации.

$ pg config

#ЭТО КОНФИГУРАЦИОННЫЙ ФАЙЛ ПОДСИСТЕМЫ АУДИТА

#НЕ РЕДАКТИРУЙТЕ ЕГО! ОН РАБОТАЕТ

#

# задание административного доступа

AUDITSCM=full

#местонахождение подсистем

AUDITSUB=/usr/opt/audit/sub

#серийный номер хэша для продукта

HASHSER=12S90AB3

#КОНЕЦ ФАЙЛА КОНФИГУРАЦИИ!!!

#

Ниже приводится сценарий, где игнорируются символы хэша:

$ pg ignore_hash

#!/bin/sh

# игнорируйте_хэш

INPUT_FILE=config

if [ -s $INPUT_FILE ]; then

while read LINE

do

case $LINE in

#*) ;; # игнорировать все символы хэша

*) echo $LINE

;;

esac

done < $INPUT_FILE

else

echo "`basename $0` : Sorry $INPUT_FILE does not exist or ls empty"

exit 1

fi

При выполнении получим:

$ ignore_hash

AUDITSCM=full

AUDITSUB=/usr/opt/audit/sub

HASHSER=12890AB3

18.7.9. Работа с форматированными отчетами

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

$ pg order

RE‑ORDER REPORT

ITEM

ORDERLEVEL

LEVEL

Pens

14

12

Pencils

15

15

Pads

7

3

Disks

3

2

Sharpeners

5

1

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

Ранее уже рассматривалось, каким образом можно игнорировать строки, начинающиеся определенными символами. Сначала производится чтение из файла. При этом игнорируются все строки, которые начинаются символами # и строками вида "ITEM". После прочтения осуществляется перенаправление данных во временный файл. Затем команда sed применяется для удаления пустых строк. На самом деле все, что делается в этом случае, напоминает фильтрацию текстового файла. Описанные действия реализованы с помощью следующего кода.

$ pg whileorder

#!/bin/sh

# whileorder

INPUT_FILE=order

HOLD=order.tmp

if [ -s $INPUT_FILE ]; then

# пустой файл вывода, добавление не производится!

>$HOLD

while read LINE

do

case $LINE in

#*|ITEM*) ;; # игнорирование строк, содержащих символы # или ITEM

*)

# перенаправление вывода во временный файл

echo $LINE >$HOLD

;;

esac

done <$INPUT_FILE

# применение команды sed для удаления пустых строк

sed -e '/^$/d' order.tmp > order.$$

mv order.$$ order.tmp

else

echo "`basename $0` : Sorry $INPUT_FILE does not exist or ls empty"

fi

В результате выполнения сценария получаем следующее:

$ pg order.tmp

Pens 14 12

Pencils 15 15

Pads 7 3

Disks 3 2

Sharpeners 5 1

Теперь остается только выполнить считывание временного файла в другом цикле while, а затем реализовать некоторые сравнения с помощью команды expr. Вот соответствующий сценарий:

$ pg whileorder2

#!/bin/sh

#whileorder2

#инициализация переменных

HOLD=order.tmp

RE_ORDER=0

ORDERS=0

STATIONERY_TOT=0

if [ -s $HOLD ]; then

echo "=========== STOCK RE_ORDER REPORT ================"

while read ITEM REORD LEVEL

do

#находимся ли мы ниже уровня переупорядочивания для данного пункта??

if [ "$LEVEL" – lt "$REORD" ]; then

да, выполняется заказ другого количества товаров

NEW_ORDER=`expr $REORD + $REORD`

# подсчет итогов по заказам

ORDERS=`expr $ORDERS + 1`

# подсчет итогов по уровням запасов

STATIONERY_TOT=`expr $STATIQNERY_TOT + $LEVEL`

echo "$ITEM need reordering to the amount $NEW_ORDER"

done <$HOLD

echo "$ORDERS new items need to be ordered"

echo "Our reorder total ls $STATIONERY_TOT"

else

echo "`basename $0` : Sorry $HOLD does not exist or ls empty"

fi

Результат выполнения сценария при обработке файла заказов.

$ whileorder

========= STOCK REORDER REPORT ===============

Pens need reordering to the amount 28

Pads need reordering to the amount 14

Disks need reordering to the amount 6

Sharpeners need reordering to the amount 10

4 new items need to be ordered

Our reorder total is 18

Не составляет особого труда скомбинировать два описанных сценария в один; в действительности изначально использовался один сценарий, который был разбит на два в учебных целях.

18.7.10. Цикл while и дескрипторы файлов

При изучении дескрипторов файлов в главе 5 уже упоминалось о том, что для считывания данных в файл применяется цикл while. С помощью дескрипторов файлов 3 и 4 следующий сценарий создает резервную копию файла myfile.txt под именем myfile.bak. Обратите внимание, что в начале сценария осуществляется проверка, которая позволяет убедиться в наличии файла. Если файл отсутствует или не содержит данные, выполнение сценария немедленно прекращается. Также обратите внимание на то, что в цикле while имеется команда null (:). Из‑за наличия этой команды цикл может выполняться бесконечно, поскольку null всегда возвращает значение "истина". При осуществлении попытки считывания по достижении конца файла отображается сообщение об ошибке. При этом выполнение сценария прекращается.

$ pg copyfile

#!/bin/sh

# copyfile

FILENAME=myfile.txt

FILENAME_BAK=myfile.bak

if [ -s $FILENAME ]; then

#открыть FILENAME для записи

#открыть FILENAME для считывания'

exec 4>$FILENAME_BAK

exec 3<$FILENAME

#бесконечный цикл до тех пор, пока имеются данные или пока не возникает

#ошибка, связанная с достижением конца файла while :

do

read LINE <&3

if [ "$?" -ne 0 ]; then

# ошибки при закрытии

exec 3<&-

exec 4<&-

exit fi

# запись в файл FILENAME_BAK

echo $LINE>&4

done else

echo "`basename $0` : Sorry, $FILENAME is not present or is empty" >&2

fi

18.8. Управление ходом выполнения циклов с помощью команд break и continue

Иногда в процессе работы возникает необходимость в прерывании или пропуске отдельных итераций цикла. При этом применяются определенные критерии. Для обеспечения подобных возможностей интерпретатор shell предлагает две команды:

   • break;

   • continue.

18.8.1. Команда break

Команда break позволяет прервать выполнение цикла. Эта команда обычно'; используются для выхода из цикла или прекращения выполнения оператора case' после осуществления некоторой обработки. Если вы окажетесь внутри вложенного цикла, можно указать количество прерываемых циклов: например, если существуют два вложенных цикла, для их прерывания используется команда break 2.

18.8.2. Прекращение выполнения оператора case

Рассмотрим следующий пример. В сценарии выполняется бесконечный цикл до тех пор, пока пользователь не введет число, большее 5. Для прерывания цикла и возврата в командную строку интерпретатора используется команда break.

$ pg breakout

#!/bin/sh

   • breakout

   • while : бесконечный цикл while :

do

echo -n "Enter any number [1..5] :"

read ANS

case $ANS in

1|2|3|4|5) echo "great you entered a number between 1 and 5"

;;

*) echo "Wrong number..bye"

break

;;

esac

done

18.8.3. Команда continue

Команда continue по своему действию напоминает команду break, за исключением одной существенной детали: она не прерывает цикл, а лишь приводит к пропуску текущей итерации цикла.

18.8.4. Пропуск строк в файлах

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

$ pg names2.txt

LISTING OF PERSONNEL FILE

TAKEN AS AT 06/1999

Louise Conrad:Accounts:ACC8987

Peter James:Payroll:PR489

Fred Terms:Customer:CUS012

Janes Lenod:Accounts:ACC887

Frank Pavely:Payroll:PR4S9

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

Также не следует принимать во внимание сведения о сотруднике с именем Peter

james Этот человек уволился из компании, но запись о нем осталась в файле.

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

Вот сценарий, выполняющий описанные задачи

$ pg whilecontinue

#!/bin/sh

# whilecontinue

SAVFDIFS=$IFS

IFS=:

INPUT_FlLE=names2.txt

NAME_HOLD="Peter James"

LINE_NO=0

if [ -s $INPUT_FILE ]; then

while read NAME DEPT ID

do

LINE_NO=`expr $LINE_NO + 1`

if [ "$LINE_NO" – le 2 ]; then

# допуск, если номер строки меньше 2 continue

if [ "$NAME"="$NAME_HOLD" ], then

# пропуск, если переменной NAME_HOLD присвоено имя Peter James

continue

else

echo " Now processing $NAME $DEPT $ID"

# обработка файла

fi

done < $INPUT_FILE

IFS=$SAVEDIFS

else

echo "`basename $0` : sorry file not found or there is no data in the file" >&2

exit 1

При выполнении сценария получим следующие результаты.

$ whilecontinue

Luise Conrad Accounts ACC8987

Fred Terns Customer CJS012

James Lenod Accounts ACC887

Frank Pavely Payroll PR389

18.9. Меню

При создании меню представляется весьма удобным использование команды null совместно с циклом while. Объединение этих конструкций приводит к бесконечному циклу, что и требуется в меню Цикл должен выполняться до тех пор, пока пользователь не осуществит выход или не выберет нужную опцию

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

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

Ниже приводится примерный вид меню.

Сначала воспользуемся подстановкой команд для назначения даты, имени хоста и пользователя. Для указания даты используется формат dd/mm/yyyy. Этот формат задается с помощью следующего параметра:

$ date +%d/%B/%Y

32/05/1999

Для указания имени хоста может применяться опция -s, которая просто отображает часть имени хоста. Иногда имя хоста задается путем присвоения соответствующей переменной полностью определенного доменного имени. Существует возможность отображения на экране меню подобного длинного имени хоста.

Присвоим переменным осмысленные имена:

MYDATE=`date +%d/%m/%Y`

THIS_H0ST=`hostname -s`

USER=`whoami`

В цикле while команда null помещена непосредственно после слова "while". Вот формат бесконечного цикла:

while : do

команды..

done

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

команда << СЛОВО

любые вводимые данные

СЛОВО

Все это исполъзуется для экрана меню. Можно также воспользоваться этой технологией для экрана помощи. Созданный экран помощи выглядит не слишком информативным.

Для обработки результатов выбора пользователя используется конструкция case. Варианты выбора меню будут следующими:

1: List files in current directory

2: Use the vi editor

3: See who ls on the system

H: Help screen

Q: Exit Menu

Оператор case должен обрабатывать все шаблоны, переводя все строчные символы шаблона в прописные. При этом устраняется риск случайного нажатия пользователем клавиши [Caps Lock], в результате чего могут искажаться вводимые данные. В случае, когда сценарий меню выполняет бесконечный цикл, пользователь нуждается в реализации элегантного выхода. В связи с этим, если пользователь выбирает клавишу [Q] или [q], сценарий должен осуществить выход с нулевым значением.

Если пользователь выполняет некорректный ввод, раздается звуковой сигнал и отображается предостерегающее сообщение. Хотя в начале этой главы упоминалось о том, что будут применяться операторы echo Linux BSD, для генерации звукового сигнала будет применена команда из версии System V:

echo '"07 the bell rang"

Конструкции echo и read применяются для задержки отображения экрана до тех пор, пока пользователь не нажмет клавишу [Enter]. При этом могут просматриваться любые сообщения или вывод команд.

В конце концов, потребуется очистить экран. Эта задача выполняется с помощью команды tput (о применении этой команды мы поговорим позже) в том случае, когда не используется clear. А теперь посмотрите на сам сценарий.

$ pg menu

#!/bin/sh # меню

# установка даты, имени пользователя и хоста

MYDATE=`date +%d/%m/%Y`

THIS_HOST= `hostname -s` USER=`whoami`

# бесконечный цикл!

while :

do

# очистка экрана с помощью команды tput

# здесь начинается конструкция "документ здесь"

cat <

# завершение конструкции "документ здесь"

echo -e -n "tYour Choice [1,2,3,H, Q] >"

read CHOICE

case $CHOICE in

1) ls

;;

2) vi

;;

3) who

;;

H|h)

# использование конструкции "документ здесь" для экрана помощи

cat <

This "ls the help screen, nothing here yet to help you!

MAYDAY

;;

Q|q) exit 0

*) echo -e "tQ07unknown user response"

;;

esac

echo -e -n "tHit the return key to continue"

read DUMMY

done

18.10. Заключение

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

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

ГЛАВА 19

Функции интерпретатора shell

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

Интерпретатор команд shell позволяет группировать наборы Команд или конструкций, создавая повторно используемые блоки. Подобные блоки называются shell–функциями.

В данной главе будут рассмотрены следующие темы:

   • определение функций;

   • работа с функциями в сценарии;

   • использование функций, определенных в файле функций;

   • примеры функций.

Функция состоит из двух частей:

Метка функции

Тело функции

В качестве метки выступает имя функции; тело функции образует набор команд. составляющих саму функцию. Имя функции должно быть уникальным: если это не так, результаты будут плачевны. Это связано с тем, что в случае наличия двух различных функций с одинаковыми именами сценарий просто не сможет вызвать нужную функцию.

Формат, применяемый для определения функций:

имя_функции ( )

{

Команда1}

или

имя_функции () {

команда 1

}

Приемлемы оба способа определения функции. Можно также использовать ключевое слово function перед именем функции имя функции.

function имя_функции ()

{

….

}

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

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

19.1. Объявление функций в сценарии

Вот пример простой функции:

hello ()

{

echo "Hello there today's date is `date`"

}

Перед использованием функций их необходимо объявить. Суть объявления заключается в том, что все функции должны быть размещены в начале кода сценария. Невозможно сослаться на функцию до тех пор, пока она не попадет в "поле зрения" интерпретатора команд. Для вызова функции требуется просто ввести ее имя. В предыдущем примере функция называлась "hello"; тело функции включало конструкцию echo, которая, в свою очередь, отображала текущую дату.

19.2. Использование функций в сценарии

После создания функции рассмотрим методы ее применения в сценарии.

$ pg fund

#!/bin/sh

# func1

hello ()

{

echo "Hello there today's date ls `date`"

)

echo "now going to the function hello"

hello

echo "back from the function"

При выполнении этого сценария получаются следующие результаты:

$ fund

now going to the function hello

Hello there today's date ls Sun Jun 6 10:46:59 GMT 2000

back from the function

В предыдущем примере функция была объявлена в начале сценария. Для обращения к функции просто вводится ее имя, которое: в данном случае звучит как "hello". После завершения выполнения функции управление возвращается следующей конструкции, которая размещена после вызова функции. В приведенном примере речь идет о конструкции echo "back from the function".

19.3. Передача параметров функции

Порядок передачи параметров функции аналогичен передаче параметров обычному сценарию. При этом используются специальные переменные $1, $2, … $9. При получении функцией переданных ей аргументов происходит замена аргументов, изначально переданных сценарию интерпретатора shell. В связи с этим неплохо было бы повторно присвоить значения переменным, получаемым функцией. В любом случае это стоит сделать, поскольку при наличии ошибок в функциях их можно будет легко обнаружить, воспользовавшись именами локальных переменных. Для вызывающих аргументов (переменных), находящихся Внутри функции, имя каждой переменной начинается с символа подчеркивания, например: _FILENAME или _filename.

19.4. Возврат значения функции

После естественного завершения выполнения функции либо в том случае, когда она завершается в результате выполнения какого‑либо условия, можно выбрать один из двух возможных вариантов:

   1. Дождаться, пока функция естественным образом не завершится сама с последующей передачей управления той части сценария, кото*рая вызвала данную функцию.

   2. Воспользоваться ключевым словом return, в результате чего будет осуществлена передача управления конструкции, которая расположена за оператором вызова функции. При этом может также указываться необязательный числовой параметр. Этот параметр принимает значение 0 в случае отсутствия ошибок и значение 1 – при наличии ошибок. Действие этого параметра аналогично действию кода завершения последней команды. При использовании ключевого слова return применяется следующий формат:

return

возвращает результат из функции, использует код завершения последней команды для проверки сосстояния

return 0

применяется при отсутствии ошибок

return 1

применяется при наличии ошибок

19.5. Проверка значений, возвращаемых функцией

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


    Ваша оценка произведения:

Популярные книги за неделю