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

                   s/:$/:./

>                   s/:/ /g'

. /usr/you/bin /bin /usr/bin

$

Мы могли бы записать это с помощью четырех отдельных команд

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

После задания каталогов в компонентах

PATH
упомянутая выше команда
test(1)
может вывести сообщение о том, существует ли файл в каждом каталоге. В принципе команда
test
— одна из самых "неуклюжих" программ UNIX. Например, команда
"test -r файл"
проверяет, существует ли файл и можно ли его читать;
"test -w файл"
проверяет, существует ли файл и можно ли в него писать, но в седьмой версии нет команды
test -х
(хотя в System V и других версиях есть), а именно она нам и нужна. Мы примем, что обращение
"test -f файл"
будет проверять, существует ли файл и не является ли он каталогом, т.е. представляет ли он собой обычный файл. Но вам следует обратиться к соответствующей странице справочного руководства, поскольку имеет хождение несколько версий.

Каждая команда вырабатывает код завершения — значение, передаваемое интерпретатору и показывающее, что произошло. Это небольшое целое число, которое устанавливается по соглашению. Так, нуль может означать "истину" (команда успешно завершена), а ненулевое значение трактуется как "ложь" (выполнение команды было неудачным). Обратите внимание на то, что выбранные здесь значения противоположны значениям истины и лжи в языке Си.

Поскольку ложь может представлять множество различных значений, причина неудачи обозначается кодом завершения по лжи. Например, команда grep возвращает 0, если произошло сопоставление, 1 — если сопоставления не было, и 2 — в случае ошибки в шаблоне или именах файлов. Каждая программа возвращает код завершения, хотя обычно нас не интересует его значение. Команда

test
неординарна: ее единственное назначение состоит в передаче кода завершения. Она ничего не выводит и не изменяет файлы.

Интерпретатор хранит код завершения последней программы в переменной

$?
:

$ cmp /usr/you/.profile /usr/you/.profile

$ 
Выдачи нет, файлы совпадают

$ echo $?

0 
0 означает успех, файлы идентичны

$ cmp /usr/you/.profile /usr/mary/.profile

/usr/you/.profile /usr/mary/.profile differ: char 6, line 3

$ echo $?

1 
He нуль означает, что файлы различны

$

У некоторых команд, таких, как

cmp
и
grep
, есть флаг
-s
, который заставляет их завершить выполнение с определенным кодом, но подавляет вывод. Оператор
if
языка
shell
запускает команды в зависимости от кода завершения некоторой команды, а именно:

if команда

then

 команды, если условие верно

else

 команды, если условие ложно

fi

Местоположение символов перевода строк очень важно:

fi
,
then
и
else
распознаются только после символа перевода строки или точки с запятой.

Оператор

if
всегда запускает команду (условие), тогда как в операторе
case
сопоставление с шаблоном производится самим интерпретатором. В некоторых версиях UNIX, включая System V,
test
является встроенной командой интерпретатора, поэтому
if
и
test
будут выполняться так же быстро, как и
case
. Если
test
— не встроенная команда, то операторы
case
более эффективны, чем операторы
if
, и следует использовать именно их для поиска шаблонов;

$ case "$1" in

hello) command

esac

выполняется быстрее, чем

if test "$1"==hello
Медленнее, если test не встроенная

then

 command

fi

Это одна из причин, по которой в языке

shell
иногда для проверки условий применяются операторы
case
, хотя в большинстве языков программирования использовались бы операторы
if
. С другой стороны, с помощью оператора
case
непросто определить, имеется ли право доступа к файлу на чтение; здесь предпочтение следует отдать команде
test
и оператору
if
.

Итак, теперь мы готовы воспользоваться первой версией команды

which
, которая выведет сообщение о том, какой файл соответствует команде:

$ cat which

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


case $# in

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

esac

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

                            s/::/:.:/g

                            s/:$/:./

                            s/:/ /g'`

do

 if test -f $i/$1 # use test -x if you can

 then

  echo $i/$1

  exit 0 # found it

 fi

done

exit 1   # not found

$

Проверим ее:

$ cx which
Сделаем ее выполняемой

$ which which

./which

$ which ed

/bin/ed

$ mv which /usr/you/bin

$ which which

/usr/you/bin/which

$

Первый оператор

case
осуществляет контроль ошибки. Обратите внимание на переключение
1>&2
в команде
echo
, которое выполняется для того, чтобы сообщение об ошибке не пропало в программном канале. Встроенная команда интерпретатора
exit
может использоваться для передачи кода завершения. В нашем примере
exit 2
передает код завершения в ситуации, когда команда не выполняется,
exit 1
— в ситуации, когда файл не удалось найти, и
exit 0
— в ситуации, когда файл найден. Если нет явного оператора
exit
, кодом завершения командного файла является код завершения последней выполняемой команды.

Что произойдет, если в вашем текущем каталоге есть программа под именем

test
? (Мы предполагаем, что
test
не является встроенной командой.)

$ echo 'echo hello' >test 
Сделаем поддельную команду test

$ cx test                 
Сделаем ее выполняемой

$ which which             
Попробуем which теперь

hello                     
Неудача!

./which

$

Вывод: требуется больший контроль. Можно запустить команду

which
(если нет команды
test
в текущем каталоге), чтобы определить полное имя для
test
и задать его явно. Но это не лучшее решение, поскольку команда
test
может присутствовать в различных каталогах в разных версиях системы, а команда
which
зависит от
sed
и
echo
, так что необходимо указать и их полные имена. Можно поступить проще — установить значение
PATH
в командном файле так, чтобы поиск команд осуществлялся только в
/bin
и
/usr/bin
. Конечно, это возможно только в команде
which
, причем прежнее значение