UNIX — страница 48 из 115

PATH
следует сохранить для определения последовательности каталогов при поиске.

$ cat which

# which cmd: which cmd in PATH is executed, final version


opath=$PATH

PATH=/bin:/usr/bin


case $# in

0) echo 'Usage: which command' 1>&2; exit 2

esac

for i in `echo $opath | sed 's/^:/.:/

                             s/::/:.:/g

                             s/ :$/:./

                             s/:/ /g'`

do

 if test -f $i/$1 # this is /bin/test

 then # or /usr/bin/test only

  echo $i/$1

  exit 0 # found it

 fi

done

exit 1   # not found

$

Теперь команда

which
выполняется даже в том случае, если есть "поддельная" команда
test
(
sed
или
echo
) среди каталогов, входящих в
PATH
.

$ ls -l test

-rwxrwxrwx 1 you 11 Oct 1 06:55 test
Все еще здесь

$ which which

/usr/you/bin/which

$ which test

./test

$ rm test

$ which test

/bin/test

$

В языке

shell
имеются две операции, объединяющие команды
||
и
&&
, использование которых часто более компактно и удобно, чем оператора
if
. Например, операция
||
может заменить некоторые операторы
if
:

test -f имя_файла || echo имя_файла не существует

эквивалентно

if test ! -f имя_файла
! обращает условие

then

 echo имя файла не существует

fi

Операция

||
, несмотря на свой вид, не имеет ничего общего с конвейерами — это обычная операция, означающая ИЛИ. Выполняется команда слева от
||
. Если ее код завершения 0 (успех), справа от
||
команда игнорируется. Если команда слева возвращает другое значение (неудача), выполняется команда справа, и значение всего выражения есть код завершения правой команды. Иными словами,
||
представляет собой обычную операцию ИЛИ, которая не выполняет свою правую часть, если левая часть завершилась успешно. Соответственно
&&
есть обычная операция И, выполняющая свою правую часть, только если левая часть завершилась успешно.

Упражнение 5.4

Почему в команде

which
перед выходом из нее не восстанавливается значение
PATH
из
opath
?

Упражнение 5.5

Если в языке

shell
используется
esac
для завершения оператора
case
и
fi
для завершения оператора
if
, почему для завершения оператора
do
применяется
done
?

Упражнение 5.6

Введите в команду

which
флаг
, чтобы выводились все файлы из
PATH
, а не только первый найденный.

Подсказка:

match='exit 0'

Упражнение 5.7

Модифицируйте команду

which
так, чтобы она учитывала встроенные в язык
shell
команды типа
exit
.

Упражнение 5.8

Модифицируйте команду

which
так, чтобы она проверяла права доступа файлов. Как изменить ее для получения диагностического сообщения, если файл не удалось найти?

5.3 Циклы
while
и
until
: контроль входа в систему

В гл. 3 цикл

for
использовался для нескольких итеративных программ. Обычно цикл
for
охватывает множество имен файлов, как в
'for i in * .с'
, или все аргументы командного файла, как в
'for i in $*'
. Но циклы в языке
shell
могут быть более общими, чем в этих идиомах, например цикл
for
в команде
which
.

Имеются три вида циклов:

for
,
while
и
until
. Чаще всего используется цикл
for
. В нем выполняется последовательность команд (тело цикла) для каждого элемента из множества слов. В большинстве случаев множество образуют просто имена файлов. В циклах
while
и
until
контроль над выполнением тела цикла осуществляется с помощью кода завершения команды. Тело цикла выполняется до тех пор, пока команда условия не вернет ненулевой код для
while
или нуль для
until
. Циклы
while
и
until
идентичны, за исключением кода завершения команды.

Ниже приведены основные формы каждого цикла:

for i in список слов

do

 тело цикла, $i последовательно получает значения элементов

done


for i (явно перечисляются аргументы командного файла, т.е. $*)

do

 тело цикла, $i последовательно получает значения аргументов

done


while команда

do

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

done


until команда

do

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

done

Вторая форма цикла

for
, в которой пустой список обозначается как
$*
, является удобным сокращением записи для наиболее типичного использования.

Командой условия, управляющей циклами

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

while sleep 60

do

 who | grep mary

done

Команда

sleep
, устанавливающая паузу на 60 с, всегда выполняется нормально (если ее не прервали) и, значит, всегда возвращает код "успех", поэтому в цикле раз в минуту будет проверяться, находится ли Мэри в системе. Недостаток такого решения состоит в том, что если Мэри уже вошла в систему, то нужно ждать 60 с, чтобы узнать об этом. О продолжении же работы Мэри в системе каждую минуту будет поступать сообщение. Цикл можно перевернуть и записать с помощью
until
, чтобы получать информацию сразу без задержки, если Мэри в данный момент работает в системе:

until who | grep mary do

 sleep 60

done

Теперь условие представляется более интересным. Если Мэри вошла в систему, то

'who | grep mary'
выдаст запись о ней из списка команды
who
и вернет код "истина". Это связано с тем, что
grep
выдает код завершения, показывающий, удалось ли ей найти что-нибудь, а код завершения конвейера есть код завершения последней команды.