Более удобный способ редактировать длинную переменную $ 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 (A "." В некоторых ситуациях закрадывается в мой PATH, черт знает откуда, и я это сделал del_path .).
Никколо М.
Здравствуй. Можно ли включить (включить) эти pathfuncs из скрипта bashrc или я должен скопировать / вставить их туда?
Криштиану
@Cristiano Либо будет работать. Это действительно зависит от вас.
Морская звезда
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

ALERT!

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

Никколо М.
источник
1
Очень хорошо. Можете ли вы принять ответ, чтобы другие не пришли сюда, чтобы предложить решение, когда вы уже нашли его? Спасибо
Basic
Дублирующие пути на самом деле не проблема. Маловероятно, что у вас будет достаточно каталогов, чтобы PATHвызвать проблемы с производительностью (особенно если оболочка кэширует успешные поиски).
Чепнер
5

Я использую функцию ниже в моем ~/.bashrc. Это то, что я получил от сисадмина в моей старой лаборатории, но я не думаю, что он их написал. Просто добавьте эти строки в ваш ~/.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
terdon
источник
Спасибо. Но я выберу решение @ Starfish, потому что его не появляется grep.
Никколо М.
2
@NiccoloM. нет проблем, примите то, что вы предпочитаете. Будьте осторожны с подходом starfish, хотя он выполнит произвольный код, evalтак что вы можете нанести серьезный ущерб, если запустите его с неверным аргументом.
Terdon
Обратите внимание , что быстрее функция существует в 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 / местные / SBIN: / USR / местные / бен: / USR / SBIN: / USR / бен: / SBIN: / бен
[-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 / местные / человек
[-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}"будет проще и проще добавить новый каталог (просто скопируйте строку и отредактируйте первую часть "d = ...."). Тем не менее, для PATH, я думаю, вы получите слишком много директорий в PATH, что не всегда хорошо (что если какая-то команда «foo» существует в одном из менее известных путей, и вы делаете что-то совершенно другое) то, что ожидают обычные пользователи?)
Оливье Дюлак
ОП попросил более краткий способ добавить пути. Это гораздо более многословно и более подвержено ошибкам, чем то, что они уже пытались избежать.
underscore_d
-1

Есть простой способ! Прочитайте функции оболочки и переменные пути в Linux Journal , 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 в файл .shar по адресу http://pastebin.ubuntu.com/13299528/.

waltinator
источник
3
Что это за функции? Как ОП их использует? Предположительно они описаны в неработающей ссылке в вашем ответе, именно поэтому мы всегда хотим, чтобы ответы были самодостаточными. Пожалуйста, отредактируйте и включите фактические функции в ваш ответ.
тердон
@terdon: ссылка работает для меня, я помещаю файл .shar в pastebin, я не буду публиковать здесь 1K строк.
Вальтинатор
Ой, у меня тоже работает. Это не когда я оставил комментарий. В любом случае, суть моего комментария заключалась в том, что мы стараемся избегать ссылок на внешние ресурсы в ответах. Мы хотим, чтобы информация была здесь, где она может быть обновлена, отредактирована и защищена от гниения ссылок.
Terdon