Вопрос или проблема
В некоторых из моих терминальных окон есть дубликаты в переменной PATH
; что-то вроде этого:
PATH=/a/b:/c/d:/a/b:/c/d:/e/f:/a/b
Я предполагаю, что виноваты строки, подобные следующей, в некоторых моих скриптах:
PATH=/a/b:$PATH
После выполнения этого и того, и снова этого PATH
становится очень длинным. Вот вопрос:
Существует ли команда bash
для очистки PATH
и подобных переменных окружения? Это должно быть скрипт на bash
, поскольку нельзя выполнить утилиту и ожидать, что она изменит окружение вызывающей оболочки.
В приведенном выше примере очищенный PATH
должен выглядеть так:
PATH=/a/b:/c/d:/e/f
Лучше не создавать дубликаты, чем пытаться удалить их позже. Это легко избежать с той техникой, которую я использую в своем .bashrc
для добавления моего личного каталога bin/
:
[ "${PATH#*$HOME/bin:}" == "$PATH" ] && export PATH="$HOME/bin:$PATH"
Я сделал это в то время, когда обновлял .bashrc
, и хотел перезапустить его без перезапуска оболочки.
Если вы хотите добавить каталог в конец $PATH
, вам нужно использовать двоеточие в начале:
[ "${PATH#*:$HOME/bin}" == "$PATH" ] && export PATH="$PATH:$HOME/bin"
Вы можете использовать расширение параметров, чтобы пройтись по PATH
и удалить дубликаты, но это будет немного сложнее, и вам нужно будет решить, какую позицию следует оставить. Что-то вроде:
OLDPATH="$PATH"; NEWPATH=""; colon=""
while [ "${OLDPATH#*:}" != "$OLDPATH" ]
do entry="${OLDPATH%%:*}"; search=":${OLDPATH#*:}:"
[ "${search#*:$entry:}" == "$search" ] && NEWPATH="$NEWPATH$colon$entry" && colon=:
OLDPATH="${OLDPATH#*:}"
done
NEWPATH="$NEWPATH:$OLDPATH"
export PATH="$NEWPATH"
После того как я написал это спонтанно и теперь протестировал, я должен был устранить большинство ошибок, и это должно быть адекватным руководством к тому, что вам нужно сделать. Оно оставляет последнее вхождение любых дубликатов, что соответствует тому, где они были бы, если бы вы использовали мой скрипт для предотвращения дубликатов с самого начала. В скрипте его, конечно, нужно вызывать с помощью команды .
/source
.
Следующее – это пример на bash.
#!/bin/bash
if [ -n "$PATH" ]; then
old_PATH=$PATH:; PATH=
while [ -n "$old_PATH" ]; do
x=${old_PATH%%:*} # первый оставшийся элемент
case $PATH: in
*:"$x":*) ;; # уже есть
*) PATH=$PATH:$x;; # еще нет
esac
old_PATH=${old_PATH#*:}
done
PATH=${PATH#:}
unset old_PATH x
fi
echo ${PATH}
Я написал этот простой скрипт на Python 3:
import os
# получаем $PATH
path = os.environ['PATH'].split(':')
# нормализуем все пути
path = map(os.path.normpath, path)
# удаляем дубликаты с помощью словаря
clean = dict.fromkeys(path)
# объединяем обратно в один путь
clean_path=":".join(clean.keys())
# вывод на stdout
print(f"PATH={clean_path}")
Я поместил скрипт в $HOME/scripts
, затем добавил эту строку в конец моего .bashrc
:
eval $(python3 $HOME/scripts/clean-path.py)
С версии Python 3.7 гарантируется, что словарь сохраняет порядок, поэтому clean_path
будет содержать каталоги в том же порядке, что и оригинальный PATH
.
С такой же неуверенностью я нашел этот вопрос полезным.
Так что я могу написать это в bash
TEST=$(echo -n $PATH | tr ":" "\n" | sort | uniq -c | tr -s " " | cut -d " " -f3 | tr "\n" ":") && echo ${TEST%:}
с выводом, вроде этого
/bin:/home/root/gowork/bin:/root/bin:/usr/local/go/bin:/usr/local/sbin:/usr/pgsql-10/bin:/usr/sbin
Вот версия с красивым выводом
if [[ -x /usr/bin/awk ]]; then
export PATH="$(echo "$PATH" | /usr/bin/awk 'BEGIN { RS=":"; } { sub(sprintf("%c$", 10), ""); if (A[$0]) {} else { A[$0]=1; printf(((NR==1) ?"" : ":") $0) }}')"
echo $PATH
else
echo "AWK не находится по адресу /usr/bin/awk" # для истинно параноидальных
fi
а вот версия в одну строку
if [[ -x /usr/bin/awk ]]; then export PATH="$(echo "$PATH" | /usr/bin/awk 'BEGIN { RS=":"; } { sub(sprintf("%c$", 10), ""); if (A[$0]) {} else { A[$0]=1; printf(((NR==1) ?"" : ":") $0) }}')" ; fi
Просто попробуйте это:
echo -e ${PATH//:/'\n'} | sort | uniq | paste -sd: -
Microsoft technet решает эту проблему с помощью скрипта PowerShell. Этот скрипт идентифицирует и удаляет дубликаты из переменной PATH
.
В нем говорится:
Иногда повторная установка программного обеспечения может добавить дублирующиеся записи в переменную окружения PATH. Поскольку у переменной окружения есть жестко закодированный предел в размере этой переменной, есть вероятность, что со временем вы можете достичь этого предела. Этот скрипт проверяет переменную окружения PATH и удаляет любые дублирующиеся записи пути.
Ответ или решение
Чтобы удалить дубликаты директорий из переменной окружения $PATH
, можно использовать несколько подходов, как в bash
, так и в Python. Эта операция важна для оптимизации рабочего окружения и предотвращения ошибок, связанных с конфликтующими версиями исполняемых файлов, расположенных в разных директориях.
Подход с использованием bash
В bash
можно создать следующий скрипт, который будет проходить по элементам в $PATH
, проверяя их на дубликаты:
#!/bin/bash
if [ -n "$PATH" ]; then
old_PATH=$PATH:; PATH=
while [ -n "$old_PATH" ]; do
x=${old_PATH%%:*} # первая оставшаяся запись
case $PATH: in
*:"$x":*) ;; # уже есть
*) PATH=$PATH:$x;; # ещё нет
esac
old_PATH=${old_PATH#*:}
done
PATH=${PATH#:} # удаление ведущего двоеточия
export PATH
fi
echo ${PATH}
Этот скрипт:
- Проверяет, не пустая ли переменная
$PATH
. - Создает временную переменную для обработки и добавляет двойное двоеточие, чтобы обеспечить правильную обработку разделителей.
- Извлекает первую запись и проверяет, была ли она уже добавлена в более новый
$PATH
. Если нет, то добавляет её. - В конце удаляет ведущий символ
:
из переменной$PATH
.
Подход с использованием Python
Если вы предпочитаете более современный и удобный способ, можно использовать Python. Вот пример простого скрипта на Python, который делает то же самое:
import os
# Получаем $PATH
path = os.environ['PATH'].split(':')
# Нормализуем все пути
path = map(os.path.normpath, path)
# Убираем дубликаты с помощью словаря
clean = dict.fromkeys(path)
# Объединяем обратно в единый путь
clean_path = ":".join(clean.keys())
# Выводим в stdout
print(f"PATH={clean_path}")
Этот скрипт:
- Разделяет переменную окружения
$PATH
на компоненты. - Нормализует пути, чтобы обеспечить консистентность.
- Удаляет дубликаты, используя словарь, который сохраняет порядок (с Python 3.7).
- Собирает пути обратно в строку и выводит результат.
Достоинства каждого подхода
- Bash-скрипт: подойдёт для тех, кто работает исключительно в Unix-подобных системах и отдает предпочтение простым, встроенным средствам оболочки.
- Python-скрипт: предоставляет более гибкий и читаемый синтаксис, подходит для пользователей, знакомых с Python. Его можно легко расширить, добавив дополнительную логику обработки.
Рекомендации
- Чтобы избежать создания дубликатов в будущем, вы можете добавлять новые директории в
$PATH
, проверяя, нет ли её уже в списке, как это описано в вашем примере с использованием.bashrc
. Это поможет предотвратить проблему с дублированием на ранних стадиях.
Таким образом, хотя удаление дубликатов из $PATH
может быть выполнено различными способами, использование скриптов, описанных выше, может значительно упростить задачу и гарантировать правильную работу вашей системы.