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'