Удалить дубликаты записей $PATH с помощью команды awk

Удалить дубликаты записей $PATH с помощью команды awk

Я пытаюсь написать функцию оболочки 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).

Связанный контент