Как я могу чисто добавить к $ PATH?

Мне хотелось бы добавить вещи в $ PATH, общесистемную или для отдельного пользователя, не добавляя один и тот же путь несколько раз.

Одна из причин, по которой вы хотите это сделать, – это добавить дополнения в .bashrc , который не требует входа в систему, а также более полезен для систем, которые используют (например) lightdm , который никогда не вызывает .profile .

Я знаю вопросы о том, как очищать дубликаты от $ PATH, но я не хочу удалять дубликаты . Я бы хотел, чтобы добавить пути, только если они еще не присутствуют.

Предположим, что новый путь, который мы хотим добавить:

 new=/opt/bin 

Затем, используя любую оболочку POSIX, мы можем проверить, нет ли new пути в пути и добавить его, если это не так:

 case ":${PATH:=$new}:" in *:$new:*) ;; *) PATH="$new:$PATH" ;; esac 

Обратите внимание на использование двоеточий. Без двоеточий мы могли бы подумать, что, скажем, new=/bin уже был на пути, потому что он сопоставлен с /usr/bin . В то время как PATH обычно имеют много элементов, также обрабатываются особые случаи нуля и один элемент в PATH. Случай с PATH, изначально не имеющий элементов (пустой), обрабатывается с помощью ${PATH:=$new} который присваивает PATH $new если он пуст. Установка значений по умолчанию для параметров таким образом является функцией всех оболочек POSIX: см. Раздел 2.6.2 документов POSIX .)

Вызываемая функция

Для удобства приведенный выше код можно поместить в функцию. Эта функция может быть определена в командной строке или, чтобы она была доступна навсегда, помещалась в сценарий инициализации вашей оболочки (для пользователей bash это будет ~/.bashrc ):

 pupdate() { case ":${PATH:=$1}:" in *:$1:*) ;; *) PATH="$1:$PATH" ;; esac; } 

Чтобы использовать эту функцию обновления пути, чтобы добавить каталог в текущий PATH:

 pupdate /new/path 

Создайте файл в /etc/profile.d , например, mypath.sh (или что хотите). Если вы используете lightdm, убедитесь, что это жизнеспособно, или используйте /etc/bashrc или файл, полученный из него. Добавьте к этому следующие функции:

 checkPath () { case ":$PATH:" in *":$1:"*) return 1 ;; esac return 0; } # Prepend to $PATH prependToPath () { for a; do checkPath $a if [ $? -eq 0 ]; then PATH=$a:$PATH fi done export PATH } # Append to $PATH appendToPath () { for a; do checkPath $a if [ $? -eq 0 ]; then PATH=$PATH:$a fi done export PATH } 

Вещи в начале (предшествующие) $ PATH имеют приоритет над тем, что следует, и, наоборот, вещи в конце (прилагаются) будут заменены тем, что было раньше. Это означает, что если ваш $ PATH является /usr/local/bin:/usr/bin и в обоих каталогах есть исполняемая gotcha , то в /usr/local/bin будет использоваться по умолчанию.

Теперь вы можете – в этом же файле, в другом файле конфигурации оболочки или из командной строки – использовать:

 appendToPath /some/path /another/path prependToPath /some/path /yet/another/path 

Если это в .bashrc , это предотвратит появление значения более одного раза при запуске новой оболочки. Существует ограничение в том, что если вы хотите добавить что-то, что было добавлено (т. Е. Переместить путь в $ PATH) или наоборот, вам придется сделать это самостоятельно.

Вы можете сделать это следующим образом:

 echo $PATH | grep /my/bin >/dev/null || PATH=$PATH:/my/bin 

Примечание: если вы создаете PATH из других переменных, убедитесь, что они не пусты, потому что многие интерпретаторы интерпретируют «как». ,

Важная часть кода – проверить, содержит ли PATH определенный путь:

 printf '%s' ":${PATH}:" | grep -Fq ":${my_path}:" 

То есть, убедитесь, что каждый путь в PATH разделен с обеих сторон разделителем PATH ( -q , затем проверьте ( -q ), существует ли литеральная строка ( -F ), состоящая из разделителя PATH , вашего пути и другого разделителя PATH там. Если это не так, вы можете смело добавить путь:

 if ! printf '%s' ":${PATH-}:" | grep -Fq ":${my_path-}:" then PATH="${PATH-}:${my_path-}" fi 

Это должно быть совместимо с POSIX и должно работать с любым путем, не содержащим символ новой строки. Это сложнее, если вы хотите, чтобы он работал с путями, содержащими новую строку, будучи совместимыми с POSIX, но если у вас есть grep который поддерживает -z вы можете это использовать.

ОБНОВИТЬ:

Я заметил, что у вашего собственного ответа была отдельная функция для добавления или добавления к $PATH . Мне понравилась эта идея. Поэтому я добавил немного обработки аргументов. Я также правильно _ namespaced это:

 _path_assign() { oFS=$IFS ; IFS=: ; add=$* ; unset PA ; A= set -- ${PATH:=$1} ; for p in $add ; do { [ -z "${p%-[AP]}" ] && { unset PA eval ${p#-}= ; continue ; } for d ; do [ -z "${d%"$p"}" ] && break done ; } || set -- ${P+$p} $* ${A+$p} done ; export PATH="$*" ; IFS=$oFS } % PATH=/usr/bin:/usr/yes/bin % _path_assign \ /usr/bin \ /usr/yes/bin \ /usr/bin/nope \ -P \ /usr/nope/bin \ /usr/bin \ -A \ /nope/usr/bin \ /usr/nope/bin % echo $PATH 

ВЫВОД:

 /usr/nope/bin:/usr/bin:/usr/yes/bin:/usr/bin/nope:/nope/usr/bin 

По умолчанию он будет -Pend на $PATH , но вы можете изменить это поведение на -P repend, добавив -P любом месте вашего списка аргументов. Вы можете переключить его обратно на -Прижалку, передав ей -A снова.

SAFE EVAL

В большинстве случаев я рекомендую людям избегать использования eval . Но это, я думаю, выделяется как пример его использования навсегда. В этом случае единственное утверждение eval может когда-либо увидеть, – P= или A= . Значения его аргументов строго проверяются до их вызова. Для этого и есть eval .

 assign() { oFS=$IFS ; IFS=: ; add=$* set -- ${PATH:=$1} ; for p in $add ; do { for d ; do [ -z "${d%"$p"}" ] && break done ; } || set -- $* $p ; done PATH="$*" ; IFS=$oFS } 

Это будет принимать столько аргументов, сколько вы его дадите, и добавьте их в $PATH только один раз и только если он еще не находится в $PATH . Он использует только полностью переносимый shell-скрипт POSIX, полагается только на встроенные оболочки и очень быстро.

 % PATH=/usr/bin:/usr/yes/bin % assign \ /usr/bin \ /usr/yes/bin \ /usr/nope/bin \ /usr/bin \ /nope/usr/bin \ /usr/nope/bin % echo "$PATH" > /usr/bin:/usr/yes/bin:/usr/nope/bin:/nope/usr/bin 

Я выполнял эту небольшую функцию со мной в разных файлах ~/.profile течение многих лет. Я думаю, что это было написано администратором в лаборатории, в которой я работал, но я не уверен. Во всяком случае, он похож на подход Голдилока, но немного отличается:

 pathmunge () { if ! echo $PATH | /bin/grep -Eq "(^|:)$1($|:)" ; then if [ "$2" = "after" ] ; then PATH=$PATH:$1 else PATH=$1:$PATH fi fi } 

Итак, чтобы добавить новый каталог в начало PATH :

 pathmunge /new/path 

и до конца:

 pathmunge /new/path after