Как редактировать переменную пути в ZSH

В моем .bash_profile есть следующие строки:

PATHDIRS="
/usr/local/mysql/bin
/usr/local/share/python
/opt/local/bin
/opt/local/sbin
$HOME/bin"
for dir in $PATHDIRS
do
    if [ -d $dir ]; then
        export PATH=$PATH:$dir
    fi
done

Однако я попытался скопировать это в свой .zshrc, и $PATH не устанавливается.

Сначала я поместил операторы echo в функцию «если каталог существует» и обнаружил, что оператор if оценивается как ложный даже для каталогов, которые явно существуют.

Затем я удалил проверку существования каталога, и $PATH был установлен неправильно, например:

/usr/bin:/bin:/usr/sbin:/sbin:
/usr/local/bin
/opt/local/bin
/opt/local/sbin
/Volumes/Xshare/kburke/bin
/usr/local/Cellar/ruby/1.9.2-p290/bin
/Users/kevin/.gem/ruby/1.8/bin
/Users/kevin/bin

Ни одна из программ в нижних каталогах не была найдена или выполнена.
Что я делаю неправильно?


zsh
person Kevin Burke    schedule 19.02.2012    source источник


Ответы (3)


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

Использование массива — лучший способ выразить это (не только в zsh, но также в ksh и bash).

pathdirs=(
    /usr/local/mysql/bin
    …
    ~/bin
)
for dir in $pathdirs; do
    if [ -d $dir ]; then
        path+=$dir
    fi
done

Поскольку вы, вероятно, не собираетесь ссылаться на pathdirs позже, вы можете также написать его в строке:

for dir in \
  /usr/local/mysql/bin \
  … \
  ~/bin
; do
  if [[ -d $dir ]]; then path+=$dir; fi
done

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

path+=/usr/local/mysql/bin
…
path=($^path(N))

N квалификатор glob выбирает только существующие совпадения. Добавьте -/ в список квалификаторов (например, (-/N) или (N-/)), если вы обеспокоены тем, что один из элементов может быть чем-то другим, кроме каталога или символической ссылки на него (например, неработающей символической ссылкой). ^ флаг расширения параметра гарантирует, что квалификатор glob применяется к каждому элементу массива отдельно. .

Вы также можете использовать квалификатор N, чтобы добавить элемент, только если он существует. Обратите внимание, что вам нужно подстановку, поэтому path+=/usr/local/mysql/bin(N) не сработает.

path+=(/usr/local/bin/mysql/bin(N-/))
person Gilles 'SO- stop being evil'    schedule 19.02.2012
comment
+1 — Два дополнительных быстрых совета, полезных для $PATH манипуляций в zsh (для всех, кто проходит мимо): (1) добавлять в начало вместо добавления: path[1,0]=/path/to/dir и (2), если вы используете подстановку для поиска каталогов, вам не нужно использовать цикл for, а вместо этого просто выполните path+=( $PWD/node_modules/**/bin ) (также работает с (1) ). - person unthought; 18.02.2013
comment
Спасибо, я воспользовался вашим предложением с path=($^path(N-/)). Теперь есть способ гарантировать, что одни и те же каталоги не дублируются в $ PATH каждый раз, когда я запускаю source ~/.zshrc ? Спасибо - person stansult; 19.02.2013
comment
@unthought ЛЮБЛЮ ваши ПРЕДВАРИТЕЛЬНЫЕ (foo[1,0]=bar) придирки. Должен любить неизвестность! - person Alex Gray; 13.03.2015

Вы можете поставить

 setopt shwordsplit

в вашем .zshrc. Затем zsh выполнит разделение мира, как это делают все оболочки Bourne. То, что значение по умолчанию равно noshwordsplit, является ошибкой, вызывающей много недоразумений. Я бы удивился, если бы это был не FAQ. Посмотрим... ага: http://zsh.sourceforge.net/FAQ/zshfaq03.html#l18 3.1. Почему $var, где var="foo bar" не выполняет ожидаемую функцию?

person Jens    schedule 19.05.2012

Все еще не уверены, в чем проблема (может быть, новые строки в $PATHDIRS)? но изменение синтаксиса массива zsh исправило это:

PATHDIRS=(
/usr/local/mysql/bin
/usr/local/share/python
/usr/local/scala/scala-2.8.0.final/bin
/opt/local/Library/Frameworks/Python.framework/Versions/2.6/bin
/opt/local/Library/Frameworks/Python.framework/Versions/2.7/bin
/opt/local/etc
/opt/local/bin
/opt/local/sbin
$HOME/.gem/ruby/1.8/bin
$HOME/bin)

и

path=($path $dir)
person Kevin Burke    schedule 19.02.2012