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을 사용하며 몇 가지 이점이 있습니다.
- 중복을 제거합니다
- 정렬 순서를 유지합니다
- 그것은 초기 모양을 유지한다 (
/usr/bin:/sbin:/usr/bin
발생합니다/usr/bin:/sbin
)