Вопрос: Добавить каталог в $ PATH, если он еще не существует


Кто-нибудь написал функцию bash, чтобы добавить директорию в $ PATH, только если ее еще нет?

Я обычно добавляю к PATH, используя что-то вроде:

export PATH=/usr/local/mysql/bin:$PATH

Если я создам свой PATH в .bash_profile, тогда он не будет читаться, если только сеанс, в котором я работаю, - это сеанс входа в систему, что не всегда верно. Если я создам свой PATH в .bashrc, он будет работать с каждой подоболочкой. Поэтому, если я запустил окно терминала, а затем запустил экран, а затем запустил сценарий оболочки, я получаю:

$ echo $PATH
/usr/local/mysql/bin:/usr/local/mysql/bin:/usr/local/mysql/bin:....

Я попытаюсь создать функцию bash, называемую add_to_path() который добавляет только каталог, если его там нет. Но, если кто-то уже написал (или нашел) такую ​​вещь, я не буду тратить на нее время.


116
2017-09-11 16:19


Источник


Видеть stackoverflow.com/questions/273909/... для некоторой инфраструктуры, которая может помочь. - dmckee
unix.stackexchange.com/questions/4965/... - Ciro Santilli 新疆改造中心 六四事件 法轮功
Если вы зададите проблему как «только добавление, если она еще не существует», вы будете грубо удивлены, когда наступит день, когда важно, чтобы вставленный элемент был в начале, но он не заканчивается там. Лучшим подходом было бы вставить элемент, а затем удалить дубликаты, поэтому, если новая запись уже была там, он будет эффективно перемещен в начало. - Don Hatch


Ответы:


Из моего .bashrc:

pathadd() {
    if [ -d "$1" ] && [[ ":$PATH:" != *":$1:"* ]]; then
        PATH="${PATH:+"$PATH:"}$1"
    fi
}

Обратите внимание, что PATH уже должен быть отмечен как экспортированный, поэтому реэкспорт не нужен. Это проверяет, существует ли каталог и является каталогом, прежде чем добавлять его, что вам может не понравиться.

Кроме того, это добавляет новый каталог в конец пути; поставить в начале, использовать PATH="$1${PATH:+":$PATH"}" вместо вышеуказанного PATH= линия.


116
2017-09-12 03:08



Мне не все равно. - Dennis Williamson
@Neil: он работает, потому что он сравнивается с ":$PATH:" а не просто "$PATH" - Gordon Davisson
@GordonDavisson: Прошу прощения, мой тест был неправильным, и вы правы. - Neil
@GordonDavisson. Какова суть материала в фигурных скобках. Кажется, я не могу разобраться в этом »${PATH:+"$PATH:"}$ 1" - boatcoder
@ Mark0978: Вот что я сделал, чтобы исправить проблему, которую указал bukzor. ${variable:+value} означает, variable определяется и имеет непустое значение, и если оно дает результат оценки value, В принципе, если PATH является непустым для начала, он устанавливает его "$PATH:$1"; если он пуст, он устанавливает его просто "$1" (обратите внимание на отсутствие толстой кишки). - Gordon Davisson


Расширяясь на ответе Гордона Дэвисона, это поддерживает несколько аргументов

pathappend() {
  for ARG in "$@"
  do
    if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]; then
        PATH="${PATH:+"$PATH:"}$ARG"
    fi
  done
}

Таким образом, вы можете сделать pathappend path1 path2 path3 ...

Для добавления,

pathprepend() {
  for ((i=$#; i>0; i--)); 
  do
    ARG=${!i}
    if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]; then
        PATH="$ARG${PATH:+":$PATH"}"
    fi
  done
}

Подобно pathappend, вы можете сделать

pathprepend path1 path2 path3 ...


19
2018-05-15 18:42



Отлично! Я сделал небольшое изменение. Для функции «pathprepend» удобно читать аргументы в обратном порядке, поэтому вы можете сказать, например, pathprepend P1 P2 P3 и в итоге PATH=P1:P2:P3, Чтобы получить это поведение, измените for ARG in "$@" do в for ((i=$#; i>0; i--)); do ARG=${!i} - ishmael
Спасибо @ishmael, хорошее предложение, я отредактировал ответ. Я понимаю, что ваш комментарий больше двух лет, но с тех пор я не вернулся. Я должен выяснить, как получить электронные почтовые рассылки, чтобы приземлиться в моем почтовом ящике! - Guillaume Perrault-Archambault


Вот что из мой ответ в этот вопрос в сочетании со структурой функции Дуга Харриса. Он использует регулярные выражения Bash:

add_to_path ()
{
    if [[ "$PATH" =~ (^|:)"${1}"(:|$) ]]
    then
        return 0
    fi
    export PATH=${1}:$PATH
}

12
2017-09-11 19:11



Это работало для меня только с использованием $1 вместо ${1} - Andrei
@Andrei: Да, скобки в этом случае не нужны. Я не уверен, почему я включил их. - Dennis Williamson


Поместите это в комментарии к выбранному ответу, но комментарии, похоже, не поддерживают форматирование PRE, поэтому добавление ответа здесь:

@ gordon-davisson Я не большой поклонник ненужного цитирования и конкатенации. Предполагая, что вы используете версию bash> = 3, вместо этого вы можете использовать встроенные в regexs bash и делать:

pathadd() {
    if [ -d "$1" ] && [[ ! $PATH =~ (^|:)$1(:|$) ]]; then
        PATH+=:$1
    fi
}

Это правильно обрабатывает случаи, когда в каталоге или в PATH есть пробелы. Возникает вопрос о том, достаточно ли достаточно встроенного в regex-движке bash, чтобы это могло быть менее эффективным, чем конкатенация и интерполяция строк, которые ваша версия делает, но почему-то это кажется мне более эстетически чистой.


10
2018-03-02 18:20



Поддержка комментариев formatting using the backtick но вы не получите никакого достойного элемента управления абзацем. - boatcoder
Это добавляет добавление в конце. Часто желательно добавить к началу, чтобы переопределить существующие местоположения. - Dennis Williamson
@DennisWilliamson Это справедливый момент, хотя я бы не рекомендовал это как поведение по умолчанию. Нетрудно понять, как измениться для добавления. - Christopher Smith
@ChristopherSmith - Re: unnecessary quoting подразумевает, что вы заранее знаете, что $PATH не является нулевым. "$PATH" делает это ОК, является ли PATH нулевым или нет. Аналогично, если $1 содержит символы, которые могут смутить парсер команд. Ввод регулярного выражения в кавычки "(^|:)$1(:|$)" предотвращает это. - Jesse Chisholm
@JesseChisholm: На самом деле, я считаю, что Кристофер указывает, что правила разные между [[ а также ]], Я предпочитаю процитировать все, что, возможно, нужно будет процитировать, если только это не приведет к его неудаче, но я считаю, что он прав, и что цитаты действительно не являются необходимый вокруг $PATH, С другой стороны, мне кажется, что вы правы $1, - Scott


idempotent_path_prepend ()
{
    PATH=${PATH//":$1"/} #delete any instances in the middle or at the end
    PATH=${PATH//"$1:"/} #delete any instances at the beginning
    export PATH="$1:$PATH" #prepend to beginning
}

Когда вам нужно $ HOME / bin, чтобы появляться ровно один раз в начале вашего $ PATH и нигде больше, не принимайте замену.


6
2017-08-17 13:31



Спасибо, это приятное элегантное решение, но я обнаружил, что должен был сделать PATH=${PATH/"... скорее, чем PATH=${PATH//"... чтобы заставить его работать. - Mark Booth
Форма с двойной косой чертой должна соответствовать любому количеству совпадений; единственная косая черта соответствует только первой (поиск «замещения шаблона» на странице bash man). Не знаю, почему это не сработало ... - andybuckley
Это не удается в необычном случае, когда $1 является единственной записью (без двоеточий). Запись будет удвоена. - Dennis Williamson
Он также удаляет слишком агрессивно, как указано PeterS6g, - Dennis Williamson


Вот альтернативное решение, которое имеет дополнительное преимущество при удалении избыточных данных:

function pathadd {
    PATH=:$PATH
    PATH=$1${PATH//:$1/}
}

Единственный аргумент этой функции добавляется к PATH, и первый экземпляр той же строки удаляется из существующего пути. Другими словами, если каталог уже существует в пути, он продвигается вперед, а не добавляется как дубликат.

Функция работает, добавив двоеточие в путь, чтобы гарантировать, что все записи имеют двоеточие спереди, а затем добавление новой записи в существующий путь с удалением этой записи. Последняя часть выполняется с использованием bash's ${var//pattern/sub} обозначения; видеть руководство по bash Больше подробностей.


6
2018-01-21 12:29



Хорошая мысль; ошибочная реализация. Подумайте, что произойдет, если у вас уже есть /home/robert в вашей PATH и ты pathadd /home/rob, - Scott


Вот мой (я считаю, что это было написано много лет назад Оскаром, системным администратором моей старой лаборатории, все ему почитали), это было в моей бареке целую вечность. Он имеет дополнительное преимущество, позволяя вам добавить или добавить новый каталог по своему усмотрению:

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

Применение:

$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin
$ pathmunge /bin/
$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin
$ pathmunge /sbin/ after
$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin:/sbin/

5
2017-08-17 18:57





Для добавления мне нравится решение @ Russell, но есть небольшая ошибка: если вы попытаетесь добавить что-то вроде «/ bin» к пути «/ sbin: / usr / bin: / var / usr / bin: / usr / local / bin: / usr / sbin «он заменяет« / bin: »3 раза (когда он вообще не подходит). Объединив исправление для этого с добавлением решения от @ gordon-davisson, я получаю следующее:

path_prepend() {
    if [ -d "$1" ]; then
        PATH=${PATH//":$1:"/:} #delete all instances in the middle
        PATH=${PATH/%":$1"/} #delete any instance at the end
        PATH=${PATH/#"$1:"/} #delete any instance at the beginning
        PATH="$1${PATH:+":$PATH"}" #prepend $1 or if $PATH is empty set to $1
    fi
}

5
2017-11-19 02:28