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 осуществляет контроль ошибки. Обратите внимание на переключение 12 в команде 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, причем прежнее значение 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' 12; 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 (успех), справа от || команда игнорируется. Если команда слева возвращает другое значение (неудача), выполняется команда справа, и значение всего выражения есть код завершения правой команды. Иными словами, || представляет собой обычную операцию ИЛИ, которая не выполняет свою правую часть, если левая часть завершилась успешно. Соответственно есть обычная операция И, выполняющая свою правую часть, только если левая часть завершилась успешно.
Почему в команде which перед выходом из нее не восстанавливается значение PATH из opath?
Если в языке shell используется esac для завершения оператора case и fi для завершения оператора if, почему для завершения оператора do применяется done?
Введите в команду which флаг -а, чтобы выводились все файлы из PATH, а не только первый найденный.
match='exit 0'