Текст книги "Linux и UNIX: программирование в shell. Руководство разработчика"
Автор книги: Дэвид Тейнсли
сообщить о нарушении
Текущая страница: 19 (всего у книги 25 страниц)
Проверим, имеются ли дубликаты для номеров служащих. Эту задачу выполняет функция check_duplicate. Если дубликаты не обнаружены, флагу DUPLICATE присваивается значение 1. Теперь нужно убедиться, что все три флага имеют значение 0 (ошибки отсутствуют). Для этого воспользуемся логической функцией and. При выполнении части then обе части уравнения должны принять истинное значение.
Если проверка завершилась успешно, значение поля подтверждается. Поскольку цикл while выполняется непрерывно, следует с помощью команды break прервать его выполнение.
if [ "$LEN_PASS" = "0" -a "$NUM_PASS" = "0" -a "$DUPLICATE" = "0"]; then
break
Если при выполнении подтверждения какой‑либо части (например, при проверке длины строки или наличия только числовых значений) возникает ошибка, то в нижней части экрана отображается соответствующее сообщение.
Staff Number: Non‑Numeric or Too Many Numbers In Field
Действительно, значение одного поля подтверждается.
Чтобы подтвердить второе и третье поля, осуществляется аналогичный процесс. Часть, отвечающая за подтверждение, содержится в другом непрерывно выполняющемся цикле while. При этом вызывается функция characters. Эта функция проверяет, содержатся ли в поле только символы. Ниже приводится фрагмент кода, в котором выполняется подтверждение имени:
while : do
echo -n "Employee's First Name :"
read F_NAME
if [ "$F_NAME" != "" ]; then
if characters $F_NAME; then
F_NAME_PASS=0
else
F_NAME_PASS=1
fi
if length_check $F_NAME 20; then
LEN_PASS=0 else
LEN_PASS=1
fi
if [ "$LEN_PASS"="0" -a "$F_NAME_PASS"="0" ]; then
break
else
echo "Staff First Name: Non‑Character or Too Many Characters In Field"
continue_prompt
fi
else
echo "Staff First Name: No Input Detected, This Field Requires Characters" continue_prompt
fi
done
Для подтверждения названия отдела (обратите внимание на приведенный ниже листинг) применяется конструкция case. Компания имеет пять отделов, а поле содержит только одно название. Обратите внимание, что названию каждого из отделов соответствуют три различных шаблона. Благодаря этому можно точно установить название отдела, если пользователь не помнит его наименование. Если совпадение с шаблоном найдено, пользователь прерывает выполнение конструкции case. Другие вводные данные перехватываются, в результате чего отображается список имеющихся отделов.
while :
do
echo -n "Company Department :"
read DEPART
case $DEPART in
ACCOUNTS|Accounts|accounts) break;;
SALES|Sales|sales) break;;
IT|It|it) break;;
CLAIMS|Claims|claims) break;;
SERVICES|Services|services) break;;
*) echo "Department: Accounts, Sales, IT, Claims, Services";;
esac
done
Когда все поля подтверждены, отображается приглашение с вопросом, следует ли сохранять эту запись. С этой целью применяется функция continue_promptYN, к которой мы уже обращались ранее, используя Y или N в качестве ответа. Если пользователь нажимает клавишу [Return], можно также передать ответ, заданный по умолчанию.
Если пользователь выбирает N, производится просмотр блоков кода в конструкции if и выполнение сценария завершается. При вводе пользователем большого числа записей следует применить функцию, которая добавляет записи в теле цикла while. Тогда после добавления записи возврат в меню не происходит, а выполнение сценария не завершается.
Если пользователь выбирает Y, запись сохраняется. Для добавления записи в файл применяется следующий код:
echo "$NUM:$F_NAME:$S_NAME:$DEPART" >> $DBFILE
Пользователь получает сообщение о том, что запись сохраняется в файле; при этом команда sleep приостанавливает выполнение сценария на одну секунду. Именно за это время пользователь сможет просмотреть сообщение.
Разделителем поля служит двоеточие. Затем файл сортируется по полю, содержащему фамилию, а поток вывода направляется в файл temp. После этого файл перемещается назад, в исходный файл DEFILE. Во время перемещений файла выполняется проверка кода завершения. При появлении каких‑либо затруднений пользователь получает соответствующее сообщение.
Вот как выглядит поток данных вывода в случае добавления записи:
ADD A RECORD
Employee Staff Number : 23233
Employee's First Name : Peter
Employee's Surname : Wills
Company Department : Accounts
Do You wish To Save This Record [Y..N] [Y]:
saved
А теперь обратите внимание, какой вид имеет файл DBFILE после добавления нескольких записей:
$ pg DBFILE
32123:Liam:Croad:Claims 2399:Piers:Cross:Accounts 239192:John:Long:Accounts 98211:Simon:Penny:Services 99202:Julie:Sittle:XT 23736:Peter:Wills:Accounts 89232:Louise:Wilson:Accounts 9l811:Andy:Wools:IT
Ниже приодится полный сценарий, выполняющий добавление записи:
$ pg dbase_add
#!/bin/sh
#dbase_add
#добавление записи
#игнорирование сигналов
trap "" 2 3 15
#файл temp содержит файлы
DBFILE=DBFILE
HOLD1=HOLD1.$$
read_a_char ()
#read_a_char
#сохранение установок
SAVEDSTTY=`stty -g`
stty cbreak
dd if=/dev/tty bs=1 count=1 2> /dev/null
stty -cbreak
stty $SAVEDSTTY
}
continue_promptYN()
#вызов: continue_prompt "string to display" default_answer
{
# continue_prompt
_STR=$1
_DEFAULT=$2
# проверка наличия корректных параметров
if [ $# -lt 1 ]; then
echo "continue_prompt: I need a string to display"
return 1
fi
while :
do
echo -n "$_STR [Y..N] [$_DEFAULT]:"
read _ANS
#если пользователь нажимает клавишу ввода, устанавливается значение
#по умолчанию, затем определяется возвращаемое значение
: ${_ANS:=$_DEFAULT}
if [ "$_ANS"="" ]; then
case $_ANS in
Y) return 0 ;;
N) return 1 ;;
esac
fi
# пользователь выбирает что–либо
case $_ANS in
y|Y|Yes|YES)
return 0 ;;
n|N|No|NO)
return 1 ;;
*) echo "Answer either Y or N, default ls $_DEFAULT" ;;
esac
echo $_ANS
done
)
continue_prompt() {
# continue_prompt
echo -n "Hit any key to continue.."
DUMMY=`read_a_char`
}
length_check ()
# length_check
# $1=строка для проверки длины $2=максимальная длина
_STR=$1
_МАХ=$2
_LENGTH=`echo $_STR | awk '{print length ($0)}`
if [ "$_LENGTH" -gt "$_MAX" ]; then
return 1 else
return 0 fi
}
a_number()
(
#a_number
#вызов: a_number $1=number _NUM=$1
_NUM=`echo $1|awk '{if($0~/[^0-9]/) print "1"}'`
if [ "$_NUM" != "" ]
then
# ошибки
return 1
else
return 0
fi
}
characters()
#characters
#вызов: char_name string {
_LETTERS_ONLY=$1
_LETTERS_ONLY=`echo $1 | awk '{if ($0~/[^a‑zA‑Z]/) print "1")'`
if [ "$_LETTERS_ONLY" != "" ]
then
# ошибки
return 1
else
# содержит только символы
return 0
fi
}
check_duplicate() {
#check_duplicate
#проверка дублирования номера служащего
#для вызова: check_duplicate строка _CODE=$1
MATCH=`grep "$_CODE>" $DBFILE`
echo $_CODE
if [ "$MATCH"="" ]; then
return 0 # нет дубликата
else
return 1 # дубликат обнаружен
fi
}
add_rec()
{
# add_rec
# == STAFF NUMBER
while :
do
echo -n "Employee Staff Number :"
read NUM
if [ "$NUM" != "" ]; then
if a_number $NUM; then
NUM_PASS=0
else
NUM_PASS=1
fi
if length_check $NUM 10; then
LEN_PASS=0
else
LEN_PASS=1
fi
# проверка наличия дубликатов… if check_duplicate $NUM; then
DUPLICATED=0
else
DUPLICATE=1
echo "Staff Number: There ls already a employee with this number" continue_prompt
fi
if [ "$LEN_PASS"="0" -a "$NUM_PASS"="0" -a "$DUPLICATE"="0" ] then
break
else
echo "Staff Number: Non‑Numeric or Too Many Numbers In Field" continue_prompt
fi
else
echo "Staff Number: No Input Detected, This Field Requires a Number" continue_prompt fi done
# == Имя
while :
do
echo -n "Employee's First Name:"
read F_NAME
if [ "$F_NAME" != "" ]; then
if characters $F_NAME; then
F_NAME_PASS=0
else
F_NAME_PASS=1
fi
if length_check $F_NAME 20; then
LEN_PASS=0
else
LEN_PASS=1
fi
# oбa условия должны быть истинными для выхода из этого цикла if [ "$LEN_PASS"="0" -a "$F_NAME_PASS"="0" ]; then
break
else
echo "Staff First Name: Non‑Character or Too Many Characters In Field" continue_prompt
fi
else
echo "Staff First Name: No Input Detected, This Field Requires Characters"
continue_prompt
fi
done
# == Фамилия
while :
do
echo -n "Employee's Surname :"
read S_NAME
if [ "$S_NAME" != "" ]; then if characters $S_NAME; then
$_NAME_PASS=0
else
$_NAME_PASS=1
fi
if length_check $S_NAME 20; then
LEN_PASS=0
else
LEN_PASS=1
fi
if [ "$LEN_PASS"="0" -a "$S_NAME_PASS"= "0" ]; then
break else
echo "Staff Surname: Non‑Character or Too Many Characters In Field" continue_prompt fi else
echo "Staff Surname: No Input Detected, This Field Requires Characters" continue_prompt fi done
# == Отдел
while :
do
echo -n "Company Department :" read DEPART case $DEPART in
ACCOUNTS|Accounts|accounts) break;;
SALES|Sales|sales) break;; IT|It|it) break;;
CLAIMS|Claims|claims) break;;
Services|SERVICES|services) break;;
*) echo "Department: Accounts, Sales, IT, Claims, Services";; esac done )
# основная программа
clear
echo -e "tttADD A EMPLOYEE RECORD"
if [ -s $DBFILE ]; then :
else
echo "Information: Creating new file to add employee records"
>$DBFILE fi
add_rec if continue_promptYN "Do You wish To Save This Record " "Y"; then
echo "$NUM:$F_NAME:$S_NAME:$DEPART" >> $DBFILE
echo "record saved"
sleep 1
sort +2 -t: $DBFILE > $HOLD1 2> /dev/nuil
if [ $? -ne 0 ]; then
echo "problems trying to sort the file..check it out"
exit 1 fi
mv $HOLD1 $DBFILE if [ $? -ne 0 ]; then
echo "problems moving the temp sort file..check it out"
exit 1 fi
else
echo " record not saved"
sleep 1 fi
22.2. Удаление записей
Прежде чем удалить запись из файла, ее нужно сначала отобразить для пользователя, чтобы он мог убедиться, что именно эта запись подлежит удалению. После получения подтверждения можно приступать к удалению записи.
При удалении записи выполняются следующие операции:
1. Поиск записи.
2. Отображение записи.
3. Подтверждение процедуры удаления.
4. Обновление файла.
Чтобы найти запись, воспользуемся полем, где содержатся фамилии. После того как пользователь введет фамилию, по которой ведется поиск, можно приступать к
обработке. Можно применить команду grep или утилиту awk. Но поскольку данный файл, скорее всего, содержит не более 100 записей, просмотрим его и определим наличие совпадений.
Если же файл содержит более двух сотен записей, желательно воспользоваться утилитой awk. Это значительно быстрее, чем просмотр файла; к тому же утилита awk более удобна при разделении полей, принадлежащих переменным.
Чтобы применить команду grep или утилиту awk, можно выполнить поиск в файле DBFILE:
echo "enter the surname to search "
read STR
#при работе с awk используйте команду
awk -F: '/$STR/' DBFILE
#при работе с grep используйте команду
grep "$STR" DBFILE
#либо команду
grep "$STR >" DBFILE
Обратите внимание, что при использовании утилиты awk переменную заключают в одинарные кавычки. Если не придерживаться этого условия, не выполняется возврат данных.
Чтобы разделить поля, можно каждому полю назначить переменные (не забывайте, что разделителем полей служит двоеточие). Переменной Ifs нужно присвоить значение двоеточия. Если не сделать этого, запись нельзя будет просматривать. При изменении значения переменной ifs желательно сначала сохранить установки. Благодаря этому их можно будет восстановить по завершении работы сценария.
Чтобы сохранить значения переменной ifs, примените следующую команду:
SAVEDIFS=$IFS
Заменить значение переменной ifs двоеточием можно, выполнив команду
IFS=:
По завершении работы с переменной IFS вы можете легко восстановить ее значение:
IFS=$SAVEDIFS
С помощью функции getrec можно выполнить полномасштабный поиск; этой функции не передаются параметры.
get_rec () {
# get_rec
clear
echo -n "Enter the employee surname :"
read STR
if [ "$STR"="q" ]; then
return 1 fi
REC=0
MATCH=no
if [ "$STR" != "" ]; then
while read CODE F_NAME S_NAME DEPART
do
REC=`expr $REC + 1` tput cup 3 4
echo -n " searching record.. $REC"
if [ "$S_NAME"="$STR" ]; then
MATCH=yes
display_rec
break
else
continue
fi
done else echo "Enter a surname to search for or q to quit" fi if [ "$MATCH"="no" ]; then no_recs fi } Пользователь может ввести фамилию или же указать q для завершения выполнения задания. Если указывается фамилия, производится проверка для того, чтобы удостовериться, что фамилия представляет собой вводные данные. Имейте в виду, что удобнее воспользоваться следующей проверкой: if [ "$STR"! = "" ]; then Но не такой: [ -z $STR ] При выборе первой проверки пользователю достаточно нажать на клавишу [Return], чтобы выполнить команду trap. Во втором случае устанавливается лишь наличие строки нулевой длины. При считывании значения каждого поля файла используются значащие наименования переменных. Затем при считывании записей применяется счетчик. Действительно, такой прием отражается только на внешней форме сценария и позволяет контролировать процесс поиска записей. Если совпадение найдено, вызывается другая процедура, которая приводит к отображению значений полей. Команда break позволяет прекратить выполнение цикла. Если совпадение не обнаружено, сценарий продолжает выполняться до следующей итерации цикла. Когда устанавливается соответствие, пользователь получает запрос, действительно ли нужно удалить запись. По умолчанию ответ будет по. if continue_promptYN "Do You Wish To DELETE This Record" "N"; then echo "DEL" grep -v $STR DBFILE >$HOLD1 2> /dev/null if [ $? -ne 0 }; then echo "Problems creating temp file $HOLD1.. check it out" exit 1 fi Удаление записи выполняется выполнением команды grep с опцией -v. В этом случае с помощью строки STR отображаются все несовпадающие поля. (Эта строка содержит фамилию, которая запрашивается пользователем при удалении записи.) Поток данных вывода для команды grep перенаправляется во временный файл, где выполняется сортировка. Затем временный файл заменяет исходный файл DBFILE. При реализации всех перемещений данных выполняется проверка с помощью кода завершения последней команды. Ниже показан поток вывода при удалении записи: Enter the employee surname :Wilson searching record,. 6 EMPLOYEE NO: 69232 FIRST NAME : Louise SURNAME : Wilson DEPARTMENT : Accounts Do You Wish To DELETE This Record [Y..N] [N] : А теперь приведем полный сценарий, выполняющий удаление записи: $ pg dbase_del #!/bin/sh #dbase_del #удаление записи #перехват сигналов trap "" 2 3 15 #Файл данных DBFILE=DBFILE #временные файлы HOLD1=HOLD1.$$ HOLD2=HOLD2.$$ continue_promptYN(} { #continue_prompt _STR=$1 _DEFAULT=$2 #проверим наличие правильных параметров if [ $# -lt 1 ]; then echo "continue_prompt: I need a string to "display" return 1 fi while : do echo -n "$_STR [Y..N] [$_DEFAULT]:" read _ANS : ${_ANS:=$_DEFAULT} if [ "$_ANS"="" ]; then case $_ANS "in Y) return 0 ;; N) return 1 ;; esac fi case $_ANS in у|Y|Yes|YES) return 0;; n|N|No|NO) return 1;; *) echo "Answer either Y or N, default is $_DEFAULT" esac done } display_rec() { #display_rec #можно воспользоваться командой cat << документ tput cup 5 3 echo "EMPLOYEE NO: $CODE" echo "FIRST NAME : $F_NAME" echo "SURNAME :$S_NAME" echo "DEPARTMENT : $DEPART" echo -e "nn" } no_recs () { # no_recs echo -e "nnSorry could not find a record with the name $STR" } get_rec () { # get_rec clear echo -n "Enter the employee surname :" read STR if [ "$STR"="q" ]; then return 1 fi REC=0 MATCH=no if [ "$STR" != "" ]; then while read CODE F_NAME S_NAME DEPART do REC=`expr $REC + 1` echo -n " searching record.. $REC" if [ "$S_NAME" = "$STR" ]; then MATCH=yes display_rec break else continue fi done << $DBFILE else echo "Enter a surname to search for or q to quit" fi if [ "$MATCH"="no" ]; then no_recs fi } SAVEDIFS=$IFS IFS=: get_rec if [ "$MATCH"="yes" ]; then if continue_promptYN "Do You Wish To DELETE This Record" "N"; then echo "DEL" grep -v $STR DBFILE >$HOLD1 2> /dev/null if [ $? -ne 0 ]; then echo "Problems creating temp file $HOLD1..check it out" exit 1 fi mv $HOLD1 DBFILE if [ $? -ne 0 ]; then echo "Problems moving temp file..check it out" exit 1 fi # сортировка файла после изменений sort +2 -t: $DBFILE >$HOLD2 2> /dev/null if [ $? -ne 0 ]; then echo "problems trying to sort the file..check it out" exit 1 fi mv $HOLD2. $DBFILE if [ $? -ne 0 ]; then echo "problems moving the temp sort file..check it out" exit 1 fi else echo "no deletion" # удаление отсутствует fi # если нужно удалить fi # если совпадает # восстановление установок IFS IFS=$SAVEDIFS 22.3. Обновление записей При рассмотрении процесса удаления записи уже обсуждался код, приводящий к обновлению записи. Когда обнаруживается корректная запись, все переменные поля записи с помощью переменной, выполняющей присваивание по умолчанию, назначаются переменной temp: : [переменная, заданная по умолчанию:=переменная) Пользователь может просто нажать клавишу [Return] для тех полей, значения которых изменять нежелательно. Затем переменная temp заменяется значением, заданным по умолчанию. Для выполнения произвольных обновлений можно просто вводить в соседние поля новые значения. echo -n -e "EMPLOYEE NO: $C0DEn" echo -n "FIRST NAME : [$F_NAME] >" read _F_NAME : ${_FNAME:=$P_NAME} Для реального обновления файла достаточно снова воспользоваться командой grep с опцией -v. Все записи обновляются по отдельности и перенаправляются во временный файл. Номер служащего задается в виде строки команды grep: grep ~v $C0DE $DBFILE >$HOLD1 Пользователь получает запрос, нужно ли сохранять данную запись. Если ответ положителен, обновленная запись также добавляется во временный файл. Затем временный файл перемещается в исходный файл DEFILE. echo "$CODE:$_F_NAME:$_S_NAME:$_DEPART" >> $HOLD1 mv $HOLD1 $DBFILE После этого выполняется сортировка файла с помощью потока вывода, который сначала перенаправлялся во временный файл, затем перемещается обратно – в исходный файл DBFILE. Проверки кода завершения выполняются во время упомянутых действий с файлами, и пользователю выдаются сообщения об имеющихся затруднениях. Ниже показан поток вывода при обновлении записи. Enter the employee surname :Penny searching record.. 7 EMPLOYEE NO: 98211 FIRST NAME : Simon SURNAME : Penny DEPARTMENT : Services ls this the record you wish to amend [Y..N] [Y]: amending EMPLOYEE NO: 98211 FIRST NAME : [Simon] > SURNAME : [Penny] > DEPARTMENT : [Services] >Accounts Ready to save this record [Y..N] [Y] : Полный сценарий, выполняющий обновление записей: $ pg dbasechange #!/bin/sh # dbasechange • обновление записи • игнорирование сигналов trap "" 2 3 15 • временные файлы DBFILE=DBFILE HOLD1=HOLD1.$$ HOLD2=HOLD2.$$ continue_promptYN() { • continue_jprompt _STR=$1 _DEFAULT=$2 • проверим, что параметры верны if [ $# -lt 1 ];. then echo "continue_prompt: I need a string to display" return 1 fi while : do echo -n "$_STR [Y..N] [$_DEFAULT]:" read _ANS : ${_ANS:=$_DEFAULT} if [ "$_ANS" = "" ]; then case $_ANS in Y) return 0 ;; N) return 1 ;; esac fi case $_ANS in y|Y|Yes|YES) return 0;; n|N|No|NO) return 1;; *) echo "Answer either Y or N, default is $_DEFAULT";; esac done } display_rec() { • отображение_записи • можно применить команду cat « документ, но нежелательно tput cup 5 3 echo "EMPLOYEE NO: $CODE" echo "FIRST NAME : $F_NAME" echo "SURNAME : $S_NAME" echo "DEPARTMENT : $DEPART" echo -e "nn" } no_recs() { # no_recs echo -e "nnSorry could not find a record with the name $STR" } get_rec () { # get_rec clear echo -n "Enter the employee surname :" read STR if [ "$STR"="q" ]; then return 1 fi REC=0 MATCH=no if [ "$STR" != "" ]; then while read CODE F_NAME S_NAME DEPART do REC=`expr $REC + 1` tput cup 3 4 echo -n " searching record.. $REC" if [ "$S_NAME"="$STR" ); then MATCH=yes display_rec break else continue fi done else echo "Enter a surname to search for or q to quit" fi if [ "$MATCH"="no" ]; then no_recs fi # основная программа SAVEDIFS=$IFS IFS=: get_rec if [ "$MATCH" = "yes" ]; then if continue_promptYN "Is this the record you wish to amend" "Y" then echo "amending" # нельзя изменить код служащего . echo -n -e "EMPLOYEE NO: $CODEn" echo -n "FIRST NAME : [$F_NAME] >" read _F_NAME : ${_FNAME:=$F_NAME} echo -n "SURNAME : [$S_NAME] >" read _S_NAME : ${_S_NAME:=$S_NAME} echo -n "DEPARTMENT : [$DEPART] >" read _DEPART : ${_DEPART:=$DEPART} grep -v $CODE $DBFILE >$HOLD1 if [ $? -ne 0 ]; then echo "Problems creating temporary file..check it out" exit 1 fi if continue_promptYN "Ready to save this record" "Y"; then echo "$CODE:$_F_NAME:$_S_NAME:$_DEPART" >> $HOLD1 mv $HOLD1 $DBFILE if [ $? -ne 0 ]; then echo "Problems moving temporary file…check it out" fi echo " Record Amended" # сортировка файла после изменений sort +2 -t: $DBFILE >$HOLD2 2> /dev/null if [ $? -ne 0 ]; then echo "problems trying to sort the file..check it out" exit 1 fi mv $HOLD2 $DBFILE if [ $? -ne 0 ]; then echo "problems moving the temp sort file..check it out" exit 1 fi else #если обновление прерывается echo "Amend aborted" exit 0 fi else # если не выполняется обновление echo "no amending" # нет удаления fi # если желательно удалить fi # если имеется совпадение IFS=$SAVEDIFS 22.4. Просмотр записей Пользователь может просматривать как все записи, так и отдельную запись. Если пользователь желает просмотреть все записи, достаточно воспользоваться конструкцией cat и awk. Если в запись входит большое количество полей, требуется более дружественный для пользователя вывод данных. Однако такой подход может негативно сказаться на других свойствах сценария. if [ "$STR"="all" ]; then echo "Surname Name Employee Code" echo "______________________________" cat $DBFILE | awk -F: '{print $2"t"$3"tt"$1}' | more return 0 fi При рассмотрении процесса удаления и обновления записей рассматривался код, приводящий к отображению отдельной записи. Небольшое изменение кода связано с тем, что при выборе пользователем этой опции, запись выводится на экран. Ниже приводится часть кода, который позволяет пересылать запись на принтер: pg << MAYDAY RECORD No : $REC EMPLOYEE NUMBER : $CODE EMPLOYEE NAME : $F_NAME EMPLOYEE SURNAME : $S_NAME EMPLOYEE DEPARTMENT : $DEPART MAYDAY Вот как выглядит поток вывода при просмотре записи: Enter the employee surname to view or all for all records:Wilson searching record… 8 EMPLOYEE NO 89232 FIRST NAME Peter SURNAME Wilson DEPARTMENT IT Do You Wish To Print This Record [Y..N] [N] : Полный сценарий, позволяющий просматривать записи, имеет следующий вид: $ pg dbaseview #!/bin/sh #dbaseview #просмотр записей #игнорирование сигналов trap "" 2 3 15 #временные файлы HOLD1=HOLD1.$$ DBFILE=DBFILE continue_promptYN () { #continue_prompt _STR=$1 _DEFAULT=$2 #проверим, что параметры верны if [ $# -lt 1 ]; then echo "continue_prompt: I need a string to display" return 1 fi while : do echo -n "$_STR [Y..N] [$_DEFAULT]:" read _ANS : $'{_ANS:=$_DEFAULT) if [ "$_ANS"="" ]; then case $_ANS in Y) return 0 ;; N) return 1 ;; esac fi case $_ANS in у|Y|Yes|YES) return 0;; n|N|No|NO) return 1;; *) echo "Answer either Y or N, default ls $_DEFAULT";; esac done } display_rec() { #diaplay_rec #можно применить команду cat <<. tput cup 5 3 echo "EMPLOYEE NO: $CODE" echo "FIRST NAME : $F_NAME" echo "SURNAME : $S_NAME" echo "DEPARTMENT : $DEPART" echo -e "nn" } no_recs () { # no_recs echo -e "nnSorry could not find a record with the name $STR" } get_rec () { # get_rec clear echo -n "Enter the employee surname to view or all for all records:" read STR if [ "$STR"="q" ] ; then return 1 fi if [ "$STR"="all" ]; then # просмотр всех записей echo "Surname Name Employee Code" echo"____________________________________" cat $DBFILE |awk -F: '{print $2"t"$3"tt"$1}' | more return 0 fi REC=0 MATCH=no if [ "$STR" != "" ]; then while read CODE F_NAME S_NAME DEPART do REC=`expr $REC + 1` tput cup 3 4 echo -n " searching record.. $REC" if [ "$S_NAME"="$STR" ]; then # обнаружено имя MATCH=yes display_rec break else continue fi done <$DBFILE else echo "Enter a surname to search for or q to quit" fi if [ "$MATCH"="no" ]; then no_recs fi } # главная программа SAVEDIFS=$IFS IFS=: get_rec if [ "$MATCH"="yes" ]; then if continue_promptYN "Do You Wish To Print This Record" "N"; then lpr << MAYDAY RECORD No: $REC EMPLOYEE NUMBER : $CODE EMPLOYEE NAME : $F_NAME EMPLOYEE SURNAME : $S_NAME EMPLOYEE DEPARTMENT : $DEPART MAYDAY else echo "No print of $S_NAME" # не отображается fi # если желательно отображение fi # если имеется совпадение IFS=$SAVEDIFS 22.5. Заключение Проверка достоверности данных, вводимых пользователем, играет важную роль. Однако для ее организации требуются некоторые дополнительные навыки. Вы имеете представление, какая вводимая информация помещается в запись, но в общем случае пользователи не располагают такими сведениями. В среде программистов известно довольно старое изречение. Звучит оно примерно так: "Мусор на входе, мусор на выходе… кого это заботит, только б не было слишком поздно". Это означает, что если не проверять входные данные сценариев, в поток вывода может попасть ненужная информация. ГЛАВА 23 Отладка сценариев Одной из самых сложных задач при создании shell–сценариев является их отладка. Желательно, чтобы пользователь, выполняющий эту задачу, получил консультации на данном этапе. Чтобы избежать распространенных ошибок, достаточно следовать указанному ниже правилу. Разбейте предлагаемый сценарий на задачи или процедуры, затем запрограммируйте и проверьте каждую процедуру и лишь потом переходите к следующему этапу. В этой главе рассматриваются следующие темы: – распространенные ошибки; – применение команды set. Действительно, ничто так не раздражает, как поиск ошибки, "спрятанной" глубоко в сценарии. Однако некоторый опыт написания сценариев поможет локализовать ошибку. Чаще всего при написании сценариев пропускаются кавычки либо ключевое слово fi в конце конструкции if. Следует учитывать, что если интерпретатор команд выдает сообщение о наличии ошибки в сценарии, нужно проанализировать не только строку, где может находиться ошибка, но также и блок кода, включающий эту строку. Интерпретатор shell не всегда точно указывает на местонахождение ошибки: сообщение об ошибке обычно появляется после выполнения строки с ошибочным оператором. 23.1. Наиболее распространенные ошибки 23.1.1. Ошибки, связанные с циклом Если сообщение об ошибке появляется при выполнении конструкций for, while, until или case, то это может означать, что фактический блок инструкций некорректно определен. Возможно, было пропущено зарезервированное слово, требуемое в данной ситуации. Ниже в сообщении об ошибке содержится слово "done", которое помогает разобраться в сути проблемы. Теперь пользователь знает, что нужно внести изменения в конструкцию while. При внимательном просмотре кода следует проверить наличие всех необходимых зарезервированных слов для конструкции while, например "do", или ключевого слова для применяемой условной конструкции. syntax error near unexpected token 'done' line 31: 'done' 23.1.2. Как обычно пропускают кавычки Второй распространенной ошибкой является элементарный пропуск кавычек. Обратите внимание на приведенный пример. Обычно приходится сталкиваться с большим количеством подобных примеров. Можно посоветовать еще раз изучить сценарий и проконтролировать наличие всех необходимых открывающих и закрывающих кавычек – unexpected EOF while looking for '""' line 36: syntax error Если требуется настроить отображение сообщения об ошибке, содержащего номер ошибочной строки, то в этом случае обычно применяется опция set nu текстового редактора vi. Это удобно, если просмотр файлов осуществляется с помощью редактора vi. Для настройки отображаемых сообщений откройте окно редактора vi, затем нажмите клавишу [Esc] и введите двоеточие. После этого выполните команду set nu и нажмите клавишу [Return]. В результате этого происходит нумерация строк и можно перейти к той строке, где, по сообщению интерпретатора shell, содержится ошибка. 23.1.3. Проверка на наличие ошибки Другой распространенной ошибкой является неправильное применение конструкции -eq. Обычно забывают указать число с какой‑либо стороны уравнения. Если поступает сообщение об ошибке, которое приводится ниже, можно сделать вывод, что произошло одно из двух событий: либо между переменной и квадратной скобкой не указан пробел, либо в квадратных скобках пропущен оператор. Вероятнее всего, в данном случае ошибка связана с первым обстоятельством. [: missing '] ' 23.1.4. Регистр символов Чаще всего причиной ошибки является неверное использование регистра при работе с переменными. Например, при присваивании переменной применяется верхний регистр, а при ссылке на нее – нижний. Тогда не следует удивляться тому, что присваивания значения не происходит. 23.1.5. Циклы for При работе с циклом for пользователи иногда забывают в части списка указать знак доллара. В результате список воспринимается как строка. 23.1.6. Команда echo При отладке сценариев чрезвычайно удобно применять команду echo. Добавьте команду echo в наиболее существенных частях сценария, где могут возникнуть какие‑либо затруднения. Например, воспользуйтесь командой echo до и после считывания или изменения значения переменной. Примените код завершения последней команды для уточнения того, успешно ли была выполнена данная команда. Следует иметь в виду, что команду echo желательно не применять перед кодом завершения последней команды, поскольку в этом случае команда всегда возвратит истинное значение. 23.2. Команда set При отладке сценария можно использовать команду set. Ниже приведены наиболее часто применяемые отладочные опции команды set. set -n Считывание, но не выполнение команд set -v Отображение всех строк при считывании set -х Отображение всех команд и их аргументов Чтобы отключить опцию set, просто замените знак – на знак +. Конечно, привычнее было бы, наоборот, знак + применять для подключения, а знак – использовать для отключения. Но здесь все зависит от привычки. Команду set можно запустить, начиная с верхней части сценария, и завершить ее выполнение по завершении сценария. Или же можно активизировать эту команду при выполнении определенной блочной конструкции, которая содержит ошибки. Рассмотрим, как функционирует команда set. Ниже приводится небольшой сценарий, который включает в список переменных определенные имена. Пользователь вводит имя, затем с помощью цикла for просматривается список в поисках соответствия. Обратите внимание, что опция set -x применяется в верхней части сценария, а в нижней части сценария ее действие завершается. $ pg error #!/bin/sh #error #установка set -x set -x LIST="Peter Susan John Barry Lucy Norman Bill Leslie"