я установил
typeset -aU path
что помогает избежать дублирования в моем PATH. Дубликат не добавляется, если я добавляю его в конец моего PATH:
path+=~/bin
path+=~/foo
path+=~/bin
После этого мой PATH содержит только одну копию моего каталога bin.
Если я поставлю каталог в начало PATH (что делается редко, но иногда необходимо), т.е.
PATH=~/bin:$PATH
В конечном итоге у меня появится дополнительная копия моего каталога bin
в моем PATH. Можно ли в этом случае также автоматически удалить дубликаты каталогов?
Я могу принудительно это сделать вручную с помощью вспомогательного массива, т.е.
temppath=($path)
path=($temppath)
но мне интересно, есть ли более простой способ сделать это.
Похоже, атрибут -U
(уникальный) можно установить отдельно для каждой переменной в паре связанных параметров, таких как path
и PATH
. Два имени по существу действуют как отдельные интерфейсы для общего бита данных, и поведение меняется, когда для установки и получения этих данных используются разные интерфейсы.
Установив -U
для path
и PATH
, оболочка должна удалять дубликаты независимо от того, как добавляются записи:
typeset -U PATH path
Это все, что абсолютно необходимо, поскольку по умолчанию атрибуты -T (связанный) и -a (массив) для path
/PATH
уже установлены. Для новой переменной, которая ведет себя аналогичным образом, объявление может выглядеть так:
typeset -aUT LD_LIBRARY_PATH ld_library_path
Тестирование:
=> typeset -aUT PATH path
=> PATH=/usr/bin
=> path+=~/bin
=> typeset -p path
typeset -aUT PATH path=( /usr/bin /Users/me/bin )
=> path+=~/foo
=> path+=~/bin
=> typeset -p path
typeset -aUT PATH path=( /usr/bin /Users/me/bin /Users/me/foo )
=> PATH=~/bin:$PATH
=> typeset -p path
typeset -aUT PATH path=( /Users/me/bin /usr/bin /Users/me/foo )
=> path=(~/foo $path)
=> typeset -p path
typeset -aUT PATH path=( /Users/me/foo /Users/me/bin /usr/bin )
Если -U
установлено по-разному для PATH
и path
:
=> path=()
=> typeset -U path; typeset +U PATH
=> typeset -p PATH path
typeset -T PATH path=( )
typeset -aUT PATH path=( )
=> PATH=/foo:/foo:/foo
=> typeset -p PATH path
typeset -T PATH path=( /foo /foo /foo )
typeset -aUT PATH path=( /foo /foo /foo )
=> path+=/bar; path+=/bar; path+=/bar
=> typeset -p PATH path
typeset -T PATH path=( /foo /bar )
typeset -aUT PATH path=( /foo /bar )
=> PATH=/foo:$PATH; PATH=/foo:$PATH;
=> typeset -p PATH path
typeset -T PATH path=( /foo /foo /foo /bar )
typeset -aUT PATH path=( /foo /foo /foo /bar )
=> path=()
=> typeset +U path; typeset -U PATH
=> typeset -p PATH path
typeset -UT PATH path=( )
typeset -aT PATH path=( )
=> path+=/alice; path+=/alice; path+=/alice
=> typeset -p PATH path
typeset -UT PATH path=( /alice /alice /alice )
typeset -aT PATH path=( /alice /alice /alice )
=> PATH=$PATH:/bob; PATH=$PATH:/bob; PATH=$PATH:/bob;
=> typeset -p PATH path
typeset -UT PATH path=( /alice /bob )
typeset -aT PATH path=( /alice /bob )
Бонус — path=($^~path(/N))
можно использовать для удаления несуществующих каталогов и недействительных записей из path
:
=> typeset -p path
typeset -aUT PATH path=( /Users/me/foo /Users/me/bin /etc/hosts /usr/bin )
=> path=($^~path(/N))
=> typeset -p path
typeset -aUT PATH path=( /Users/me/bin /usr/bin )
${^...}
- эмулировать раскрытие скобок. Каждый элемент массива дополняется окружающим текстом, например: /Users/me/foo(/N) /Users/me/bin(/N) /etc/hosts(/N) /usr/bin(/N)
.${~...}
- расширение глобуса. Полученные строки используются в качестве шаблонов glob.(/)
— включать в глобальные расширения только каталоги.(N)
— разрешить пустые результаты. Итак:/Users/me/foo(/N)
расширяется до нуля/ноля, поскольку его не существует./Users/me/bin(/N)
становится /Users/me/bin
./etc/hosts(/N)
имеет значение null, потому что это файл./usr/bin(/N)
становится /usr/bin
.path=(...)
— сбрасывает path
на все, что осталось от расширения. Если для -U
установлено path
, то добавляются только уникальные значения.MacOS иногда добавляет бессмысленные записи через /etc/paths.d
, это способ их очистить.
Опцию -U
можно установить отдельно для PATH
и path
— я добавил в ответ несколько примеров вместе с другими примечаниями. Вероятно, нам не понадобится ~
в дополнении path=($^~path(/N))
, но я не проверял каждый сценарий.
Я не думаю, что это правильно. Я установил -U только для path
, и когда я обновляю path
справа (конец массива), он становится уникальным, и это отражается в PATH
, и наоборот. Использование зш 5.8.1
Что произойдет, если вы обновите его, используя PATH
вместо path
, и что показывают typeset -p PATH
и typeset -p path
? В документации zsh
для -U
PATH
и path
называются отдельными интерфейсами, что, возможно, является лучшим способом представления о них. Приведенные выше тесты проводились с zsh 5.9.
Действительно! Я проверил и смог проверить это и для своего zsh.
path
иPATH
— связанные переменные. То, что вы делаете с одним, вы можете сделать и с другим. Я просто использую написание в зависимости от того, что мне удобнее печатать. Установка-U
для обеих переменных не представляет никакой опасности, но не имеет никакого эффекта. Однако ваша идея избавиться от временного массива классная. Я не думал об этом. Не могли бы вы также объяснить синтаксис$^~path
?~
будет расширять тильду внутри переменной PATH (IMO ненужно), но как работает^
? Я предполагаю, что это отвечает за удаление дубликатов.