В давние времена многоархитектурных Unix-окружений разработчики дистрибутивов не могли прийти к единому мнению о том, что должно быть в $PATH
. Базовые вещи, вроде /bin
и /usr/bin
, были везде одинаковыми, но у каждого дистрибутива был собственный набор дополнительных директорий (у Solaris их, например, было много). Кроме того — у разных локальных вычислительных групп было различное видение того, где должны размещаться локальные программы. Например — в /usr/local/bin
, в /local/bin
, в /opt/<something>/bin
, в /<group>/bin
и так далее. Всё это усложняло мне жизнь, так как я занимался поддержкой общего набора dot-файлов, используемых во всех Unix-системах, за которые я отвечал, и мне не хотелось бы, чтобы моя переменная $PATH
представляла бы собой огромный список, содержащий пути ко всем необходимым директориям каждой из систем. Поэтому мне нужно было убирать всё лишнее из гигантского базового списка директорий, которые могли присутствовать в $PATH
, оставляя там лишь те директории, которые существовали в текущей системе. А чтобы ещё сильнее усложнить эту задачу, мне хотелось использовать для этого только команды, встроенные в оболочку, и это — при работе с оболочкой, где test
встроенной командой не является.
К моему счастью, есть одна команда, которая должна быть встроенной в оболочку и при этом даёт сбой в том случае, если директории не существует (или если пользователь не может с ней работать). Это — команда cd
. Использование cd
в качестве замены 'test -d'
— это решение немного странное, но работоспособное. В моей оболочке были настоящие списки, поэтому я мог добиться того, что мне нужно, примерно так:
# то, что может попасть в $PATH, находится в $candidates
path=`{ tpath=()
for (pe in $candidates)
builtin cd $pe >[1=] >[2=] && tpath=($tpath $pe)
echo $tpath }
(С относительными путями этот код не работает, но в моей переменной $PATH
таких путей не было.)
Так как во всех оболочках обязательно должна быть встроенная команда cd
, тот же подход можно было использовать практически во всех оболочках. Bourne-подобные оболочки, правда, усложняли задачу по сборке $PATH
. Там, как минимум, нужно было добавлять :'s
между элементами (cf) и, возможно, в эквиваленте $candidates
для таких оболочек нужно было бы использовать :'s
между записями, что привело бы к необходимости разделять записи, основываясь на этой конструкции.
(В оболочке Bourne я представил бы $candidates
в виде строки, заключённой в кавычки, элементы которой разделены пробелами, так как работать с таким списком директорий гораздо проще. Правда, при таком подходе я не смог бы обрабатывать $PATH-записи, содержащие пробелы, но таких записей в $PATH
обычно не бывает.)
Использование cd
вышеописанным образом — это, по сути, хак, но хаки — это то, к чему мы вынуждены прибегать в минималистичных окружениях оболочек, когда необходимо, для решения неких задач, обойтись без внешних программ. Я же, на самом деле, написал на C маленькую программу, isdirs
, которая решала вышеописанную задачу, и использовал её в тех системах, с которыми я работал достаточно часто для того, чтобы оправдать компиляцию для них этой программы. А код, в котором использовалась команда cd
, был чем-то вроде запасного варианта, применяемого в системах и в ситуациях, в которых я не мог воспользоваться моей isdirs
.
(Этот материал можно счесть чем-то вроде продолжения одной моей статьи про хак командной оболочки Unix, которая тоже связана с кросс-архитектурной средой и с dot-файлами.)
P.S. То, о чём я рассказал, происходило в те времена, когда системы были достаточно медленными для того чтобы тот, кто их обслуживает, стремился бы к тому, чтобы без крайней нужды не пользоваться дополнительными внешними программами в dot-файлах оболочки. Именно поэтому я и решил пользоваться только встроенными командами оболочки вместо того, чтобы выполнять множество вызовов test
или чего-то подобного. А в большинстве современных оболочек test
— это встроенная в них команда.
Приходилось ли вам, при работе в Unix, решать какие-то задачи, необычным образом пользуясь исключительно встроенными командами оболочки?
Автор: ru_vds