Я пытаюсь написать функцию оболочки bash, которая позволит мне удалять дубликаты каталогов из переменной среды PATH.
Мне сказали, что это можно сделать с помощью однострочной команды, используя awk
команду, но я не могу понять, как это сделать. Кто-нибудь знает, как?
решение1
Если у вас еще нет дубликатов PATH
и вы хотите добавить только те каталоги, которых там еще нет, вы можете легко сделать это с помощью одной только оболочки.
for x in /path/to/add …; do
case ":$PATH:" in
*":$x:"*) :;; # already there
*) PATH="$x:$PATH";;
esac
done
А вот фрагмент оболочки, который удаляет дубликаты из $PATH
. Он проходит по записям одну за другой и копирует те, которые еще не были просмотрены.
if [ -n "$PATH" ]; then
old_PATH=$PATH:; PATH=
while [ -n "$old_PATH" ]; do
x=${old_PATH%%:*} # the first remaining entry
case $PATH: in
*:"$x":*) ;; # already there
*) PATH=$PATH:$x;; # not there yet
esac
old_PATH=${old_PATH#*:}
done
PATH=${PATH#:}
unset old_PATH x
fi
решение2
ВотпонятныйРешение в одну строку, которое делает все правильно: удаляет дубликаты, сохраняет порядок путей и не добавляет двоеточие в конце. Поэтому оно должно дать вам дедуплицированный PATH, который дает точно такое же поведение, как и оригинал:
PATH="$(perl -e 'print join(":", grep { not $seen{$_}++ } split(/:/, $ENV{PATH}))')"
Он просто разбивается по двоеточию ( split(/:/, $ENV{PATH})
), использует uses grep { not $seen{$_}++ }
для фильтрации любых повторяющихся вхождений путей, за исключением первого, а затем объединяет оставшиеся пути, разделяя их двоеточиями, и выводит результат ( print join(":", ...)
).
Если вам нужна дополнительная структура, а также возможность дедупликации других переменных, попробуйте этот фрагмент, который я в настоящее время использую в своей собственной конфигурации:
# Deduplicate path variables
get_var () {
eval 'printf "%s\n" "${'"$1"'}"'
}
set_var () {
eval "$1=\"\$2\""
}
dedup_pathvar () {
pathvar_name="$1"
pathvar_value="$(get_var "$pathvar_name")"
deduped_path="$(perl -e 'print join(":",grep { not $seen{$_}++ } split(/:/, $ARGV[0]))' "$pathvar_value")"
set_var "$pathvar_name" "$deduped_path"
}
dedup_pathvar PATH
dedup_pathvar MANPATH
Этот код удалит дубликаты как PATH, так и MANPATH, и вы сможете легко вызывать dedup_pathvar
другие переменные, содержащие списки путей, разделенные двоеточиями (например, PYTHONPATH).
решение3
Вот изящный вариант:
printf %s "$PATH" | awk -v RS=: -v ORS=: '!arr[$0]++'
Длиннее (чтобы увидеть, как это работает):
printf %s "$PATH" | awk -v RS=: -v ORS=: '{ if (!arr[$0]++) { print $0 } }'
Хорошо, поскольку вы новичок в Linux, вот как на самом деле задать PATH без завершающего ":"
PATH=`printf %s "$PATH" | awk -v RS=: '{ if (!arr[$0]++) {printf("%s%s",!ln++?"":":",$0)}}'`
Кстати, убедитесь, что в вашем PATH НЕТ каталогов, содержащих «:», иначе все будет испорчено.
некоторая заслуга:
решение4
Пока мы добавляем не-awk однострочники:
PATH=$(zsh -fc "typeset -TU P=$PATH p; echo \$P")
(Могло бы быть и проще, PATH=$(zsh -fc 'typeset -U path; echo $PATH')
но zsh всегда считывает как минимум один zshenv
файл конфигурации, который может изменять PATH
.)
Он использует две полезные функции zsh:
- скаляры, привязанные к массивам (
typeset -T
) - и массивы, которые автоматически удаляют повторяющиеся значения (
typeset -U
).