awk 명령을 사용하여 중복된 $PATH 항목 제거

awk 명령을 사용하여 중복된 $PATH 항목 제거

PATH 환경 변수에서 디렉터리의 중복 복사본을 제거할 수 있는 bash 셸 함수를 작성하려고 합니다.

이 명령을 사용하여 한 줄 명령으로 이 작업을 수행할 수 있다는 말을 들었지만 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})콜론( )으로 분할하고 use를 사용하여 첫 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가 아닌 oneliner를 추가하는 한:

PATH=$(zsh -fc "typeset -TU P=$PATH p; echo \$P")

(간단할 수도 있지만 zsh는 항상 수정할 수 있는 PATH=$(zsh -fc 'typeset -U path; echo $PATH')최소한 하나의 구성 파일을 읽습니다 .)zshenvPATH

두 가지 훌륭한 zsh 기능을 사용합니다.

  • 배열과 관련된 스칼라( typeset -T)
  • 및 중복된 값을 자동으로 제거하는 배열( typeset -U).

관련 정보