Я трохи здивований, що ще ніхто про це не згадав, але ви можете використовувати 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 / шлях / до / мій / бін / реж
$ pathappend .
$ echo "$ PATH"
<your_old_path> : / шлях / до / мій / бін / реж
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
ПАТ, то це добре; ми можемо просто залишити його таким, яким він є. Але, якщо цього /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
вдруге.
3. З чим угоди if ARGA=$(readlink -f "$ARG")
?
Якщо це незрозуміло, цей рядок перевіряє, чи readlink
вдалося це зробити. Це просто хороша практика оборонного програмування. Якщо ми збираємось використовувати вихід з команди m
як частину команди n (де m < n ), доцільно перевірити, чи не вдалося команду m та якось обробити це. Я не думаю, що це, ймовірно, readlink
не вдасться, але, як обговорювалось у розділі Як отримати абсолютний шлях довільного файлу з ОС X
та інших країн, readlink
це винахід GNU. Це не визначено в POSIX, тому його доступність у Mac OS, Solaris та інших нелінуксних Unix викликає сумніви. (Насправді я просто прочитав коментар, в якому сказано:readlink -f
схоже, він не працює на Mac OS X 10.11.6, але realpath
працює з вікна. "Отже, якщо ви працюєте в системі, яка не має readlink
, або де readlink -f
вона не працює, ви можете змінити це сценарій для використання realpath
.) Встановлюючи мережу безпеки, ми робимо наш код дещо більш портативним.
Звичайно, якщо ви працюєте в системі, яка не має readlink
(або realpath
), ви не збираєтесь цього робити .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
}