Удалить дублирующиеся записи из переменной PATH

9

Я часто изменяю свой .bashrc и затем поставляю его. Однако, когда у меня есть что-то вроде export PATH="~/bin:~/perl5/bin:$PATH"моего файла, PATHпеременная окружения увеличивается каждый раз, когда я получаю исходный файл.

Например, при первом обращении к источнику .bashrc PATHпеременная состоит из ~/bin:~/perl5/bin:/usr/bin:/bin.

Второй раз он состоит из ~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

В третий раз он состоит из ~/bin:~/perl5/bin:~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

Есть ли простой способ сделать так, чтобы он добавлял только то, чего еще нет в PATH?

Кристофер Боттомс
источник

Ответы:

12

Используйте функцию pathmunge (), доступную в большинстве дистрибутивов /etc/profile:

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

редактировать : для zshпользователей, typeset -U <variable_name>будет дедупликация записи пути.

Сэм Халике
источник
Спасибо! Это не в /etc/profileDebian Lenny, поэтому я включаю его в свой .bashrc.
Кристофер Боттомс
Использование: pathmunge /some/pathразместится /some/pathв начале $PATHи pathmunge /some/path afterбудет /some/pathв конце$PATH
Кристофер Боттомс
Уборщик способ сделать проверку , чтобы увидеть , если каталог разрубать существует в текущем пути, в современном Баше оболочках: if ! [[ $PATH =~ (^|:)$1($|:) ]] ; then.
Кристофер Кашелл
Я бы не использовал это. Если моя наивная версия сказала PATH = / foo: $ PATH, это означает, что я хочу / foo победить. pathmunge /foo beforeне достигает этого Лучшим способом было бы добавить / foo в начале, а затем удалить дубликаты.
Дон Хэтч
3

У меня была эта проблема, поэтому я использовал комбинацию методов, перечисленных в вопросе StackOverflow . Ниже приведено то, что я использовал для дедупликации фактической переменной PATH, которая уже была установлена, поскольку я не хотел изменять базовый скрипт.

    tmppath=(${PATH// /@})
    array=(${tmppath//:/ })
    for i in "${array[@]//@/ }"
    do
        if ! [[ $PATH_NEW =~ "$i" ]]; then
            PATH_NEW="${PATH_NEW}$i:";
        fi
    done;
    PATH="${PATH_NEW%:}"
    export PATH
    unset PATH_NEW

Вы всегда можете оптимизировать это немного больше, но у меня был дополнительный код в моем оригинале, чтобы показать, что происходит, чтобы убедиться, что он правильно устанавливает переменные. Еще одна вещь, которую стоит отметить, это то, что я выполняю следующее

  1. заменить любой символ пробела символом @
  2. разбить массив
  3. цикл через массив
  4. заменить любые символы @ в строке элемента пробелом

Это сделано для того, чтобы я мог обрабатывать каталоги с пробелами (в домашних каталогах Samba с именами пользователей Active Directory могут быть пробелы!)

netniV
источник
ВНИМАНИЕ: В этой реализации есть серьезная ошибка !! он будет удален, /binесли есть другие записи, например, /usr/binпотому что регулярное выражение совпадает, даже если эти две записи не совпадают!
Sukima
Вот альтернативная реализация, которая исправляет эту ошибку: github.com/sukima/dotfiles/blob/…
Sukima
1

Установите ваш путь явно.

Cakemox
источник
Спасибо. Я попытался установить путь явно, но у меня есть файл .bashrc, который я использую в нескольких средах, поэтому точное значение по умолчанию PATHне всегда одинаково.
Кристофер Боттомс
1

Только одна строка:

for i in $(echo $PATH|tr ":" "\n"|sort|uniq);do PATH_NEW="${PATH_NEW}$i:";done;PATH="${PATH_NEW%:}"
bindbn
источник
Спасибо. Одна из причин, по которой мне трудно это сделать, заключается в том, что в алфавитном порядке (или ASCIIbetically) переупорядочивается содержимое $PATH. Мне нравится помещать определенные каталоги в начале $PATH(например /home/username), чтобы мои личные копии исполняемых файлов запускались вместо встроенных по умолчанию.
Кристофер Боттомс
1

Я могу думать о двух разных способах решения этой проблемы. Первый - запустить ваш .bashrc со строки, которая явно устанавливает ваш базовый PATH. Таким образом, каждый раз, когда вы его создаете, он сбрасывается в базовый перед добавлением дополнительных каталогов.

Например, добавьте:

# Reset the PATH to prevent duplication and to make sure that we include
# everything we want.
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Кроме того, вы можете проверить элемент, прежде чем добавить его в путь. Чтобы сделать это, вы должны использовать что-то вроде:

if ! [[ $PATH =~ '~/perl5/bin' ]]
then
    PATH="~/perl5/bin:$PATH"
fi

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

Примечание: второй вариант может работать только так, как написано в современных версиях bash. Поддержка регулярных выражений не является функцией Bourne Shell (/ bin / sh) и может отсутствовать в других оболочках. Кроме того, использование кавычек может не потребоваться или может даже вызвать проблемы в некоторых новейших версиях bash.

Кристофер Кашелл
источник
Спасибо. Я попытался установить путь явно, но у меня есть файл .bashrc, который я использую в нескольких средах, поэтому точное значение по умолчанию PATHне всегда одинаково.
Кристофер Боттомс
Вы все еще можете справиться с этим, проверив сценарий, чтобы увидеть, какое у вас локальное имя хоста, а затем задав соответствующий абсолютный путь для этого сервера.
Кристофер Кашелл
Просто опечатка! отсутствует в if. Кроме того, на удивление (для меня) легко превратить это в функцию, которая перебирает аргументы, разделенные пробелами, поэтому вы можете просто сказать: add_to_PATH ~/perl5/bin ~/.bin unix.stackexchange.com/a/4973/28760 (я думаю, что caseиспользуемое здесь утверждение более переносимо , но твой ifмне понятнее). РЕДАКТИРОВАТЬ странное совпадение, что этот вопрос также добавляет Perl5!
13-го
@ 13ren: Спасибо за примечание. Я также только что понял, что использовал одинарные кавычки в строке набора PATH вместо двойных кавычек, как мне следовало бы. Как написано, он бы заменил ваш существующий путь на имя переменной. К сожалению! Очевидно это было плохой записью для меня для опечаток; оба исправлены сейчас.
Кристофер Кашелл
0

Вот мое решение: PATH=$(echo -n $PATH | awk -v RS=: -v ORS=: '!x[$0]++' | sed "s/\(.*\).\{1\}/\1/")

Хороший легкий лайнер, который не оставляет следов:

AJ.
источник