awk 명령으로 중복 $ PATH 항목 제거 할 수있는

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

명령을 사용하여 한 줄 명령 으로이 작업을 수행 할 수 있다고 말 awk했지만 어떻게 수행 해야하는지 알 수 없습니다. 아무도 어떻게 알아?



답변

에 중복 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


답변

다음 은 모든 올바른 작업을 수행 하는 이해 하기 쉬운 단일 라이너 솔루션입니다. 중복을 제거하고 경로 순서를 유지하며 끝에 콜론을 추가하지 않습니다. 따라서 원본과 정확히 동일한 동작을 제공하는 중복 제거 된 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)을 보유하는 다른 변수를 쉽게 호출 할 수 있습니다 .


답변

매끄러운 것이 있습니다 :

printf %s "$PATH" | awk -v RS=: -v ORS=: '!arr[$0]++'

더 길게 (작동 방식보기) :

printf %s "$PATH" | awk -v RS=: -v ORS=: '{ if (!arr[$0]++) { print $0 } }'

좋아, 리눅스를 처음 사용하기 때문에, 뒤에 “:”없이 실제로 PATH를 설정하는 방법이있다

PATH=`printf %s "$PATH" | awk -v RS=: '{ if (!arr[$0]++) {printf("%s%s",!ln++?"":":",$0)}}'`

btw는 PATH에 “:”를 포함하는 디렉토리가 없어야합니다. 그렇지 않으면 엉망이됩니다.

약간의 크레딧 :


답변

다음은 AWK 원 라이너입니다.

$ PATH=$(printf %s "$PATH" \
     | awk -vRS=: -vORS= '!a[$0]++ {if (NR>1) printf(":"); printf("%s", $0) }' )

어디:

  • printf %s "$PATH"$PATH줄 바꿈없이 내용을 인쇄합니다.
  • RS=: 입력 레코드 구분 문자를 변경합니다 (기본값은 줄 바꿈).
  • ORS= 출력 레코드 분리 문자를 빈 문자열로 변경
  • a 내재적으로 작성된 배열의 이름
  • $0 현재 레코드를 참조
  • a[$0] 연관 배열 역 참조입니다.
  • ++ 증가 후 연산자입니다
  • !a[$0]++ 오른쪽을 보호합니다. 즉, 현재 레코드가 인쇄되지 않은 경우에만 인쇄되도록합니다.
  • NR 1로 시작하는 현재 레코드 번호

즉, AWK는 구분 문자를 PATH따라 내용 을 분할 :하고 순서를 수정하지 않고 중복 항목을 필터링하는 데 사용됩니다 .

AWK 연관 배열은 해시 테이블로 구현되므로 런타임은 선형 적입니다 (즉, O (n)).

: 변수 :에서 이름으로 디렉토리를 지원 하기 위해 인용 부호제공하지 않기 때문에 인용 문자를 찾을 필요 가 없습니다PATH .

Awk + 붙여 넣기

페이스트로 위를 단순화 할 수 있습니다.

$ PATH=$(printf %s "$PATH" | awk -vRS=: '!a[$0]++' | paste -s -d:)

paste명령은 awk 출력을 콜론으로 분산시키는 데 사용됩니다. 이것은 인쇄에 대한 awk 동작 (기본 동작)을 단순화합니다.

파이썬

파이썬 2 라이너와 동일 :

$ PATH=$(python3 -c 'import os; from collections import OrderedDict; \
    l=os.environ["PATH"].split(":"); print(":".join(OrderedDict.fromkeys(l)))' )


답변

이것에 대해서도 비슷한 토론이 있었습니다 .

나는 조금 다른 접근법을 취합니다. 설치되는 다른 모든 초기화 파일에서 설정된 PATH를 그대로 사용하는 대신 getconf시스템 경로를 식별하고 먼저 배치 한 다음 선호하는 경로 순서를 추가 한 다음 awk중복을 제거하는 데 사용 하는 것이 좋습니다 . 이것은 실제로 명령 실행 속도를 높이거나 높이 지 않을 수도 있지만 이론적으로 더 안전합니다.

# I am entering my preferred PATH order here because it gets set,
# appended, reset, appended again and ends up in such a jumbled order.
# The duplicates get removed, preserving my preferred order.
#
PATH=$(command -p getconf PATH):/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
# Remove duplicates
PATH="$(printf "%s" "${PATH}" | /usr/bin/awk -v RS=: -v ORS=: '!($0 in a) {a[$0]; print}')"
export PATH

[~]$ echo $PATH
/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/usr/lib64/ccache:/usr/games:/home/me/bin


답변

우리는 비 ak oneliners를 추가하는 한 :

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).

답변

PATH=`perl -e 'print join ":", grep {!$h{$_}++} split ":", $ENV{PATH}'`
export PATH

이것은 perl을 사용하며 몇 가지 이점이 있습니다.

  1. 중복을 제거합니다
  2. 정렬 순서를 유지합니다
  3. 그것은 초기 모양을 유지한다 ( /usr/bin:/sbin:/usr/bin발생합니다 /usr/bin:/sbin)