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}))으로 분할하고 사용을 사용하여 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")

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

두 가지 멋진 zsh 기능을 사용합니다.

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

관련 정보