Я немного удивлен, что никто еще не упомянул об этом, но вы можете использовать readlink -f
чтобы преобразовать относительные пути в абсолютные и добавить их в PATH как таковые.
Например, чтобы улучшить ответ Гийома Перро-Аршамбо,
pathappend() {
for ARG in "$@"
do
if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]; then
PATH="${PATH:+"$PATH:"}$ARG"
fi
done
}
становится
pathappend() {
for ARG in "$@"
do
if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]
then
if ARGA=$(readlink -f "$ARG") #notice me
then
if [ -d "$ARGA" ] && [[ ":$PATH:" != *":$ARGA:"* ]]
then
PATH="${PATH:+"$PATH:"}$ARGA"
fi
else
PATH="${PATH:+"$PATH:"}$ARG"
fi
fi
done
}
1. Основы - что хорошего это делает?
Команда readlink -f
(среди прочего) преобразует относительный путь в абсолютный.
Это позволяет вам сделать что-то вроде
$ cd /path/to/my/bin/dir
$ pathappend .
$ echo "$PATH"
<your_old_path>:/path/to/my/bin/dir
2. Почему мы дважды тестируемся на наличие PATH?
Хорошо, рассмотрим приведенный выше пример.
Если пользователь говорит pathappend .
из каталога /path/to/my/bin/dir
во второй раз будет ARG
.
,
Конечно .
не будет присутствовать в PATH
. Но тогда ARGA
будет установлен в /path/to/my/bin/dir
(абсолютный эквивалент пути .
), Который уже находится в PATH
. Поэтому нам нужно избегать добавления /path/to/my/bin/dir
к PATH
во второй раз.
Возможно, что еще более важно, основная цель readlink
состоит в том, чтобы, как следует из названия, посмотреть на символическую ссылку и прочитать путь, который она содержит (т.е. указывает на).
Например:
$ ls -ld /usr/lib/perl/5.14
-rwxrwxrwx 1 root root Sep 3 2015 /usr/lib/perl/5.14 -> 5.14.2
$ readlink /usr/lib/perl/5.14
5.14.2
$ readlink -f /usr/lib/perl/5.14
/usr/lib/perl/5.14.2
Теперь, если вы говорите pathappend /usr/lib/perl/5.14
, и у вас уже есть /usr/lib/perl/5.14
в вашей переменной PATH, это нормально; мы можем просто оставить все как есть.
Но если /usr/lib/perl/5.14
еще нет в вашем PATH, мы вызываем readlink
и получаем ARGA
= /usr/lib/perl/5.14.2
, а затем добавляем это в PATH
. Но подождите минуту - если вы уже сказали, что pathappend /usr/lib/perl/5.14
, то у вас уже есть /usr/lib/perl/5.14.2
в вашей PATH, и, опять же, нам нужно проверить это, чтобы избежать добавления это PATH
во второй раз.
3. В чем дело, if ARGA=$(readlink -f "$ARG")
?
В случае, если неясно, эта строка проверяет успешность readlink
ссылки.
Это просто хорошая, защитная практика программирования.
Если мы собираемся использовать выходные данные команды m как часть команды n (где m < n), целесообразно проверить, не завершилась ли команда m, и обработать ее каким-либо образом.
Я не думаю, что вполне вероятно, что readlink
потерпит неудачу - но, как обсуждалось в разделе Как извлечь абсолютный путь произвольного файла из OS X и других источников, readlink
- это изобретение GNU.
Он не указан в POSIX, поэтому его доступность в Mac OS, Solaris и других не-Linux Unix-системах вызывает сомнения.
Установив систему безопасности, мы делаем наш код несколько более переносимым.
Конечно, если вы работаете в системе, которая не имеет readlink
, вам не захочется делать pathappend .
,
Второй -d
тест ([ -d "$ARGA" ]
) действительно, вероятно, не нужен.
Я не могу представить ни одного сценария, в котором $ARG
- это каталог, а readlink
успешен, но $ARGA
- это не каталог.
Я просто скопировал и вставил первый оператор if
чтобы создать третий, и оставил там тест -d
из-за лени.
4. Любые другие комментарии?
Да уж.
Как и многие другие ответы здесь, этот тест проверяет, является ли каждый аргумент каталогом, обрабатывает его, если он есть, и игнорирует его, если это не так.
Это может (или не может) быть адекватным, если вы используете pathappend
только в « .
»(Например, .bash_profile
и .bashrc
) и другие скрипты.
Но, как показал этот ответ (выше), вполне возможно использовать его в интерактивном режиме.
Вы будете очень озадачены, если вы делаете
$ pathappend /usr/local/nysql/bin
$ mysql
-bash: mysql: command not found
Вы заметили, что я сказал nysql
в команде pathappend
, а не mysql
?
И этот pathappend
ничего не сказал; он просто молча проигнорировал неверный аргумент?
Как я уже говорил выше, это хорошая практика для обработки ошибок.
Вот пример:
pathappend() {
for ARG in "$@"
do
if [ -d "$ARG" ]
then
if [[ ":$PATH:" != *":$ARG:"* ]]
then
if ARGA=$(readlink -f "$ARG") #notice me
then
if [[ ":$PATH:" != *":$ARGA:"* ]]
then
PATH="${PATH:+"$PATH:"}$ARGA"
fi
else
PATH="${PATH:+"$PATH:"}$ARG"
fi
fi
else
printf "Error: %s is not a directory.\n" "$ARG" >&2
fi
done
}