Вирізати дублікати записів зі змінної 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. замініть будь-який символ SPACE символом @
  2. розділити масив
  3. петлю через масив
  4. замініть будь-які символи @ у рядку елементів пробілом

Це для того, щоб я міг обробляти каталоги з пробілами в (домашні каталоги Samba з іменами користувачів Active Directory можуть мати пробіли!)


ПОПЕРЕДЖЕННЯ. У цій реалізації є велика помилка !! він буде видалений, /binякщо є інші записи, наприклад, /usr/binтому що регулярний вирівнювання збігається, навіть якщо два записи не однакові!
Сукіма

Ось альтернативна версія,
Sukima

1

Встановіть свій явний шлях.


Дякую. Я спробував встановити шлях явно, але у мене є .bashrc файл, який я використовую в декількох середовищах, тому точний за замовчуванням PATHне завжди однаковий.
Крістофер Ниж

1

Лише одна рядок:

for i in $(echo $PATH|tr ":" "\n"|sort|uniq);do PATH_NEW="${PATH_NEW}$i:";done;PATH="${PATH_NEW%:}"

Дякую. Мене важко спричинити за те, що це в алфавітному порядку (або 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не завжди однаковий.
Крістофер Дно

Ви все ще можете впоратися з цим, перевіривши в скрипті, щоб побачити, яке місцеве ім’я хоста, а потім встановити абсолютний шлях відповідним чином для цього сервера.
Крістофер Кашелл

Просто помилка: the! відсутня в if. Крім того, на диво (для мене) легко перетворити це на функцію, яка перетворюється на аргументи, розділені пробілом, так що ви можете просто сказати: add_to_PATH ~/perl5/bin ~/.bin unix.stackexchange.com/a/4973/28760 (я думаю, що caseвикористовуване твердження є більш портативним , але твоє ifмені зрозуміліше). EDIT дивним збігом обставин, що це питання також додає perl5!
13н

@ 13ren: Дякую за замітку. Я також щойно зрозумів, що використовував одинарні лапки у встановленому рядку PATH, а не подвійні лапки, як я мав би мати. Як було написано, він би замінив ваш існуючий шлях на ім'я змінної. На жаль! Мабуть, це було поганим для мене записом для друкарських помилок; обидва виправлені зараз.
Крістофер Кашелл

0

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

Приємний простий вкладиш, який не залишає слідів:

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.