Зручніший спосіб редагувати довгий $ PATH?


35

Я хочу додати в ~ / .bashrc кілька каталогів до моєї $ PATH.

Мій $ PATH досить довгий, тому трохи важко зрозуміти, які каталоги вони містять і в якому порядку.

Я знаю, що можу змінити свій ~ / .bashrc таким чином:

PATH=$PATH:/some/dir
PATH=$PATH:/another/dir:/yet/another
PATH=$PATH:/and/another
...

це полегшило б читання. Але мені було цікаво, чи за останні роки Bash придбав якийсь синтаксис, який полегшує визначення довгого PATH. Наприклад, я фантазую про синтаксис, подібний до:

PATH=:((
  /some/dir
  /another/dir
  /yet/another
  /and/another
  ...
))

Я знаю, що такий синтаксис недійсний. Мені було цікаво, чи є щось таке просто. Є там?


Традиційний підручник для встановлення шляху через PATH=foo:$PATHздається неправильним, оскільки він щоразу підтримує зростання source ~/.bashrcі навіть exec bashне може допомогти, оскільки $PATHє export.
林果 皞

Відповіді:


25

Я використовую набір функцій зручності для попереднього додавання або додавання шляху до змінної. Функції надходять у тарбол розподілу для Bash у файлі contrib під назвою "pathfuncs".

  • add_path додасть запис до кінця змінної PATH
  • pre_path додасть запис до початку змінної PATH
  • del_path видалить запис зі змінної PATH, де б вона не була

Якщо вказати змінну як другий аргумент, вона використовуватиме її замість PATH.

Для зручності ось вони:

# is $1 missing from $2 (or PATH) ?
no_path() {
    eval "case :\$${2-PATH}: in *:$1:*) return 1;; *) return 0;; esac"
}
# if $1 exists and is not in path, append it
add_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="\$${2:-PATH}:$1"
}
# if $1 exists and is not in path, prepend it
pre_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="$1:\$${2:-PATH}"
}
# if $1 is in path, remove it
del_path () {
  no_path $* || eval ${2:-PATH}=`eval echo :'$'${2:-PATH}: |
    sed -e "s;:$1:;:;g" -e "s;^:;;" -e "s;:\$;;"`
}

Якщо ви додасте їх до файлу запуску bash, ви можете додати його до PATH так:

pre_path $HOME/bin
add_path /sbin
add_path /usr/sbin

Або вкажіть іншу змінну:

pre_path $HOME/man MANPATH
pre_path $HOME/share/man MANPATH
add_path /usr/local/man MANPATH
add_path /usr/share/man MANPATH

Я використовую цей метод у своїх rc-файлах, ставлячи перше pre_paths, а add_paths - друге. Це робить усі зміни мого шляху легко зрозуміти з першого погляду. Ще одна перевага полягає в тому, що рядки є досить короткими, що я можу додати прострочний коментар до рядка, якщо це необхідно.

А оскільки це функції, ви можете використовувати їх інтерактивно з командного рядка, наприклад, сказавши, add_path $(pwd)щоб додати поточний каталог до шляху.


Спасибі. Я перевірив ваш код, і він працює. Дивно, але я також знайшов застосування del_path ("". "Впадає в мою ПАТУ в деяких ситуаціях. Чорт знає звідки, так я і зробив del_path .).
Нікколо М.

Привіт. можна джерело (включити) цей pathfuncs з скрипту bashrc або мені слід скопіювати / вставити їх туди?
Кріштіано

@ Cristiano Either буде працювати. Це справді залежить від вас.
Морська зірка

11

Гаразд, я зрозумів таке рішення, яке, на мою думку, є елегантним (наскільки синтаксис оболонки йде). Він використовує синтаксис масиву Bash, а також акуратний спосіб приєднання елементів:

paths=(
  /some/dir
  /another/dir
  '/another/dir with spaces in it'
  /yet/another
  /and/another
  /end
)
paths_joined=$( IFS=: ; echo "${paths[*]}" )

PATH=$paths_joined:$PATH

АЛЕРТ!

Виявляється, в цьому рішенні є проблема : на відміну від рішень @terdon та @Starfish, він не спочатку перевіряє, чи шляхи вже є в PATH. Отже, оскільки я хочу помістити цей код у ~ / .bashrc (а не у ~ / .profile), дублюючі шляхи будуть переповзати в PATH. Тому не використовуйте це рішення (якщо ви не помістите його в ~ / .profile (або, краще, ~ / .bash_profile, оскільки він має синтаксис Bash).


1
Дуже хороша. Чи можете ви прийняти відповідь, щоб інші не приходили сюди, щоб запропонувати рішення, коли ви її вже знайшли? Спасибі
Основна

Дублікати шляхів насправді не є проблемою. Навряд чи вам доведеться додати достатньо каталогів, щоб PATHнасправді викликати проблеми з продуктивністю (тим більше, що оболонка кешує успішні пошуки).
чепнер

5

Я використовую функцію нижче в своєму ~/.bashrc. Це щось, що я отримав від sysadmin в моїй старій лабораторії, але я не думаю, що він їх написав. Просто додайте ці рядки до свого ~/.profileчи ~/.bashrc:

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

Це має різні переваги:

  • Додавання нових каталогів в $PATHтривіальна: pathmunge /foo/bar;
  • Це дозволяє уникнути дублювання записів;
  • Ви можете вибрати, чи потрібно додати новий запис до початку ( pathmunge /foo/barабо до кінця ( pathmunge /foo/barпісля) $PATH.

Файл ініціалізації вашої оболонки буде містити щось на зразок:

pathmunge /some/dir
pathmunge /another/dir
pathmunge '/another/dir with spaces in it'
pathmunge /yet/another
pathmunge /and/another
pathmunge /end

Спасибі. Але я підберу рішення @ Starfish, оскільки його не породжує grep.
Нікколо М.

2
@NiccoloM. немає жодних проблем, прийміть те, що вам більше подобається. Будьте обережні з підходом до морських зірок, але він виконує довільний код, evalщоб ви могли завдати серйозної шкоди, якщо запустити його з неправильним аргументом.
тердон

Зауважте, що в Redhat існує швидша функція, щоб зробити це без зовнішньої команди grep, див. Bugzilla.redhat.com/show_bug.cgi?id=544652#c7
林果 皞

4

Я хочу додати в ~ / .bashrc кілька каталогів до моєї $ PATH.

Я використовую наступне у Cygwin. Він повинен працювати в інших версіях bash. Ви можете вилучити, unset PATHщоб наростити його на поточному PATH(якщо ви це зробите, можливо, доведеться зрозуміти, як правильно додати :роздільники).

Примітка:

  • Я колись мав цю функцію у bashфункції, але втратив її після збою диска.

У моєму .bash_profile:

# Build up the path using the directories in ~/.path_elements
unset PATH
while read line; do 
  PATH="${PATH}$line"; 
done < ~/.path_elements

...

# Add current directory to path
export PATH=".:${PATH}"

В ~/.path_elements:

/home/DavidPostill/bin:
/usr/local/bin:
/usr/bin:
/c/Windows/system32:
/c/Windows

Спасибі. Ваша відповідь надихнула мене працювати на подібне рішення. (На мій смак, зберігання шляхів в окремому файлі викликає занепокоєння.)
Нікколо М.

1

Я використовую це в моєму .bashrc (а також моєму .zshrc, оскільки я зазвичай використовую zsh там, де це доступно замість bash). Звичайно, він вимагає від мене вручну додавати каталоги, але перевага полягає в тому, що, коли я його оновлюю, я можу продовжувати копіювати його на нові сервери і не турбуватися про PATH на новому сервері, створеному з каталогами, які там відсутні.

##
## ПАТ
##
## Замість того, щоб просто клопотати наш PATH з каталогами, які можуть
## не підходить для цього сервера, намагайтеся бути розумним щодо того, що ми додаємо
##
PATH = / usr / local / sbin: / usr / local / bin: / usr / sbin: / usr / bin: / sbin: / bin
[-d / cs / sbin] && PATH = / cs / sbin: $ PATH
[-d / cs / bin] && PATH = / cs / bin: $ PATH
[-d / usr / ucb] && PATH = $ PATH: / usr / ucb
[-d / usr / ccs / bin] && PATH = $ PATH: / usr / ccs / bin
[-d / usr / local / ssl / bin] && PATH = $ PATH: / usr / local / ssl / bin
[-d / usr / krb5 / bin] && PATH = $ PATH: / usr / krb5 / bin
[-d / usr / krb5 / sbin] && PATH = $ PATH: / usr / krb5 / sbin
[-d / usr / kerberos / sbin] && PATH = $ PATH: / usr / kerberos / sbin
[-d / usr / kerberos / bin] && PATH = $ PATH: / usr / kerberos / bin
[-d /cs/local/jdk1.5.0/bin] && PATH = $ PATH: /cs/local/jdk1.5.0/bin
[-d /usr/java/jre1.5.0_02/bin] && PATH = $ PATH: /usr/java/jre1.5.0_02/man
[-d /usr/java1.2/bin] && PATH = $ PATH: /usr/java1.2/bin
[-d /cs/local/perl5.8.0/bin] && PATH = $ PATH: /cs/local/perl5.8.0/bin
[-d / usr / perl5 / bin] && PATH = $ PATH: / usr / perl5 / bin
[-d / usr / X11R6 / bin] && PATH = $ PATH: / usr / X11R6 / bin
[-d / etc / X11] && PATH = $ PATH: / etc / X11
[-d / opt / sfw / bin] && PATH = $ PATH: / opt / sfw / bin
[-d / usr / local / apache / bin] && PATH = $ PATH: / usr / local / apache / bin
[-d / usr / apache / bin] && PATH = $ PATH: / usr / apache / bin
[-d / cs / admin / bin] && PATH = $ PATH: / cs / admin / bin
[-d / usr / openwin / bin] && PATH = $ PATH: / usr / openwin / bin
[-d / usr / xpg4 / bin] && PATH = $ PATH: / usr / xpg4 / bin
[-d / usr / dt / bin] && PATH = $ PATH: / usr / dt / bin

Я роблю те ж саме для моєї MANPATH:

##
## МАНПАТ
##
## Замість того, щоб просто клопотати нашу MANPATH з каталогами, які можуть бути
## не підходить для цього сервера, намагайтеся бути розумним щодо того, що ми додаємо
##
MANPATH = / usr / місцевий / man
[-d / usr / share / man] && MANPATH = $ MANPATH: / usr / share / man
[-d / usr / local / share / man] && MANPATH = $ MANPATH: / usr / local / share / man
[-d / usr / man] && MANPATH = $ MANPATH: / usr / man
[-d / cs / man] && MANPATH = $ MANPATH: / cs / man
[-d / usr / krb5 / man] && MANPATH = $ MANPATH: / usr / krb5 / man
[-d / usr / kerberos / man] && MANPATH = $ MANPATH: / usr / kerberos / man
[-d / usr / local / ssl / man] && MANPATH = $ MANPATH: / usr / local / ssl / man
[-d /cs/local/jdk1.5.0/man] && MANPATH = $ MANPATH: /cs/local/jdk1.5.0/man
[-d /usr/java/jre1.5.0_02/man] && MANPATH = $ MANPATH: /usr/java/jre1.5.0_02/man
[-d /usr/java1.2/man] && MANPATH = $ MANPATH: /usr/java1.2/man
[-d / usr / X11R6 / man] && MANPATH = $ MANPATH: / usr / X11R6 / man
[-d / usr / local / apache / man] && MANPATH = $ MANPATH: / usr / local / apache / man
[-d / usr / local / mysql / man] && MANPATH = $ MANPATH: / usr / local / mysql / man
[-d /cs/local/perl5.8.0/man] && MANPATH = $ MANPATH: /cs/local/perl5.8.0/man
[-d / usr / perl5 / man] && MANPATH = $ MANPATH: / usr / perl5 / man
[-d / usr / local / perl / man] && MANPATH = $ MANPATH: / usr / local / perl / man
[-d /usr/local/perl5.8.0/man] && MANPATH = $ MANPATH: /usr/local/perl5.8.0/man
[-d / usr / openwin / man] && MANPATH = $ MANPATH: / usr / openwin / man

Окрім того, що я маю єдиний файл, який я можу скопіювати до систем у різних середовищах, не боячись додавати до PATH неіснуючі каталоги, цей підхід також має перевагу, що дозволяє мені вказати порядок, у який я хочу, щоб каталоги відображалися в PATH. Оскільки перший рядок кожного визначення повністю переосмислює змінну PATH, я можу оновити свій .bashrc та джерело його після редагування, щоб оновити свою оболонку без додавання дублікатів (що я раніше відчував, коли я просто починав із " $ PATH = $ PATH: / new / dir ". Це гарантує отримання чистої копії в бажаному порядку.


1
пропонуючи альтернативу: d="/usr/share/man" ; [ -d "$d" ] && MANPATH="$MANPATH:${d}"буде коротше і простіше додати новий dir (просто скопіюйте рядок і відредагуйте першу частину "d = ...."). Однак, для PATH, я думаю, ви закінчитеся із значно великою кількістю грядок у вашій PATH, що не завжди добре (що робити, якщо якась команда "foo" існує в одному з менш відомих шляхів, і зробите щось зовсім інше що очікує постійний користувач?)
Олів'є Дулак

ОП попросила більш короткий спосіб додати шляхи. Це набагато більш багатослівно і більш схильне до помилок, ніж те, чого вони вже намагалися уникнути.
підкреслити_

-1

Є простий спосіб! Прочитайте функції оболонки та змінні контуру в журналі Linux , 01 березня 2000 р. Стівен Коллер

Функції дозволяють мені використовувати новий тип даних у моєму середовищі bash - список, розділений двокрапкою. Окрім PATH, я використовую їх для налаштування моїх LOCATE_PATH, MANPATH та інших, і як загальний тип даних у програмі bash. Ось як я налаштував свій PATH (за допомогою функцій):

# Add my bin directory to $PATH at the beginning, so it overrides 
addpath -f -p PATH $HOME/bin

# For Raspberry Pi development (add at end)
addpath -b -p PATH ${HOME}/rpi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin

# remove nonexistent directories from PATH
delpath -n -p PATH

# ensure PATH contains unique entries
uniqpath -p PATH

Оскільки посилання на журнал Linux називається "зламаним", я помістив функції Bash Path у файл .shar за адресою http://pastebin.ubuntu.com/13299528/


3
Які це функції? Як ОП використовує їх? Імовірно, вони описані у розірваному посиланні у вашій відповіді, і саме тому ми завжди хочемо, щоб відповіді містилися в собі. Будь ласка, відредагуйте та включіть у свою відповідь фактичні функції.
тердон

@terdon: посилання працює для мене, я помістив .shar файл на пастібін, я не публікую тут 1K рядки.
waltinator

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