Bash의 $ PATH 변수에서 경로를 제거하는 가장 우아한 방법은 무엇입니까?


114

또는보다 일반적으로 Bash 환경 변수의 콜론으로 구분 된 목록에서 항목을 제거하려면 어떻게해야합니까?

나는 몇 년 전에 Bash 변수 확장의 더 발전된 형태를 사용하는 간단한 방법을 보았다고 생각했지만, 그렇다면 그것을 잃어 버렸다. Google을 빠르게 검색 한 결과 놀랍게도 관련성있는 결과가 거의 없었고 "단순"또는 "우아함"이라고 부르는 결과는 없었습니다. 예를 들어, 각각 sed 및 awk를 사용하는 두 가지 방법 :

PATH=$(echo $PATH | sed -e 's;:\?/home/user/bin;;' -e 's;/home/user/bin:\?;;')
PATH=!(awk -F: '{for(i=1;i<=NF;i++){if(!($i in a)){a[$i];printf s$i;s=":"}}}'<<<$PATH)

간단한 것이 없습니까? Bash에서 split () 함수와 유사한 것이 있습니까?

업데이트 :
의도적으로 모호한 질문에 대해 사과해야 할 것 같습니다. 좋은 토론을 유도하는 것보다 특정 사용 사례를 해결하는 데 관심이 적었습니다. 다행스럽게도 알았습니다!

여기에는 매우 영리한 기술이 있습니다. 결국 다음 세 가지 기능을 도구 상자에 추가했습니다. 마법은 주로 Martin York의 awkRS 변수 의 영리한 사용에 기반한 path_remove에서 발생 합니다.

path_append ()  { path_remove $1; export PATH="$PATH:$1"; }
path_prepend () { path_remove $1; export PATH="$1:$PATH"; }
path_remove ()  { export PATH=`echo -n $PATH | awk -v RS=: -v ORS=: '$0 != "'$1'"' | sed 's/:$//'`; }

거기에있는 유일한 진짜 균열 sed은 후행 결장을 제거하기 위해 사용하는 것입니다 . 그러나 Martin의 나머지 솔루션이 얼마나 간단한 지 고려할 때 나는 기꺼이 함께 살기를 원합니다!


관련 질문 : 쉘 스크립트에서 $ PATH 요소를 어떻게 조작합니까?


모든 변수에 대해 : WORK=`echo -n ${1} | awk -v RS=: -v ORS=: '$0 != "'${3}'"' | sed 's/:$//'`; eval "export ${2}=${WORK}"하지만 func $VAR VAR pattern(@ martin-york 및 @ andrew-aylett 기반) 다음과 같이 호출해야합니다.
vesperto

답변:


51

awk로 1 분 :

# Strip all paths with SDE in them.
#
export PATH=`echo ${PATH} | awk -v RS=: -v ORS=: '/SDE/ {next} {print}'`

편집 : 아래 의견에 대한 응답 :

$ export a="/a/b/c/d/e:/a/b/c/d/g/k/i:/a/b/c/d/f:/a/b/c/g:/a/b/c/d/g/i"
$ echo ${a}
/a/b/c/d/e:/a/b/c/d/f:/a/b/c/g:/a/b/c/d/g/i

## Remove multiple (any directory with a: all of them)
$ echo ${a} | awk -v RS=: -v ORS=: '/a/ {next} {print}'
## Works fine all removed

## Remove multiple including last two: (any directory with g)
$ echo ${a} | awk -v RS=: -v ORS=: '/g/ {next} {print}'
/a/b/c/d/e:/a/b/c/d/f:
## Works fine: Again!

보안 문제에 대한 응답으로 편집 : (질문과 관련이 없음)

export PATH=$(echo ${PATH} | awk -v RS=: -v ORS=: '/SDE/ {next} {print}' | sed 's/:*$//')

이렇게하면 .경로에 효과적으로 추가되는 마지막 항목을 삭제하여 남은 후행 콜론이 제거 됩니다.


1
마지막 요소 또는 여러 요소를 제거하려고 할 때 실패합니다. 첫 번째 경우에는 현재 dir (빈 문자열로, 잠재적 인 보안 구멍)을 추가하고 두 번째 경우에는``를 경로 요소로 추가합니다.
Fred Foo 2011 년

1
@larsmans : 잘 작동합니다. 참고 : Empty는 "./"인 현재 디렉토리와 동일하지 않습니다.
Martin York

2
의는 "회원"으로 빈 문자열 PATH변수가 않습니다 , 특별한 규칙으로, 그것은 여전히 않기 때문에 적어도 V7 유닉스 1979의 모든 유닉스 셸 나타낸다 현재 디렉토리 bash. 설명서를 확인하거나 직접 시도하십시오.
Fred Foo 2011 년

1
@Martin : POSIX이 동작을 필요로하지만 문서를 수행하고 그것을 허용하지 않습니다 pubs.opengroup.org/onlinepubs/9699919799/basedefs/...
프레드 푸

1
이것으로 마지막 요소를 제거 할 때 더 미묘한 문제가 있습니다. awk는 문자열 끝에 널 바이트를 추가하는 것 같습니다 . 나중에 PATH에 다른 디렉토리를 추가하면 실제로 검색되지 않습니다.
sschuberth

55

내 더러운 해킹 :

echo ${PATH} > t1
vi t1
export PATH=$(cat t1)

18
가장 확실한 솔루션을 때 그것은 좋은 기호 결코 -automate 과정. :-D
Ben Blank

"우아한"대안보다 훨씬 안전합니다.
Jortstek

44

대체의 큰 문제는 최종 사건이기 때문에 최종 사례를 다른 케이스와 다르지 않게 만드는 것은 어떨까요? 경로의 시작과 끝 부분에 이미 콜론이있는 경우 콜론으로 둘러싼 원하는 문자열을 검색 할 수 있습니다. 그대로 콜론을 쉽게 추가하고 나중에 제거 할 수 있습니다.

# PATH => /bin:/opt/a dir/bin:/sbin
WORK=:$PATH:
# WORK => :/bin:/opt/a dir/bin:/sbin:
REMOVE='/opt/a dir/bin'
WORK=${WORK/:$REMOVE:/:}
# WORK => :/bin:/sbin:
WORK=${WORK%:}
WORK=${WORK#:}
PATH=$WORK
# PATH => /bin:/sbin

순수한 bash :).


2
: 나는 몇 가지 여분의 설탕이 튜토리얼 섹션을 추가 것 tldp.org/LDP/abs/html/string-manipulation.html
사이버 올리베이라

1
가장 간단한 해결책처럼 보였기 때문에 이것을 사용했습니다. 매우 빠르고 쉬웠으며 실제로 PATH 변수를 변경하는 마지막 줄 바로 앞에서 echo $ WORK로 작업을 쉽게 확인할 수 있습니다.
Phil Gran

2
절대적으로 작은 보석. 이 게시물을 발견했을 때 제가하려고했던 것입니다. -앤드류 감사합니다! BTW : 공백 ( "/ usr / bin"에 대해 동일)과 마지막 줄 "$ WORK"를 포함해야하는 경우를 대비하여 ": $ PATH :"주위에 큰 따옴표를 추가하고 싶을 수도 있습니다.

2
감사합니다, @ PacMan-- :). 나는 확신 당신의 할당을위한 공간이 필요하지 않습니다 (다만 그것을 시도)하고있어 WORKPATH라인이 변수 할당 및 명령 실행을위한 섹션으로 분석 한 후 변수 확장이 발생있다. REMOVE따옴표가 필요할 수도 있고, 상수 인 경우 문자열을 바로 대체 할 수도 있습니다.
Andrew Aylett

문자열이 보존 될 때 항상 혼란스러워서 항상 문자열을 큰 따옴표로 묶기 시작했습니다. 이를 명확히 해주셔서 다시 한 번 감사드립니다. :)

26

내가 고안 할 수있는 가장 간단한 솔루션은 다음과 같습니다.

#!/bin/bash
IFS=:
# convert it to an array
t=($PATH)
unset IFS
# perform any array operations to remove elements from the array
t=(${t[@]%%*usr*})
IFS=:
# output the new array
echo "${t[*]}"

위의 예는 "usr"을 포함하는 $ PATH의 모든 요소를 ​​제거합니다. "* usr *"를 "/ home / user / bin"으로 대체하여 해당 요소 만 제거 할 수 있습니다.

sschuberth업데이트

공백 $PATH끔찍한 아이디어 라고 생각하지만 여기에 처리하는 솔루션이 있습니다.

PATH=$(IFS=':';t=($PATH);n=${#t[*]};a=();for ((i=0;i<n;i++)); do p="${t[i]%%*usr*}"; [ "${p}" ] && a[i]="${p}"; done;echo "${a[*]}");

또는

IFS=':'
t=($PATH)
n=${#t[*]}
a=()
for ((i=0;i<n;i++)); do
  p="${t[i]%%*usr*}"
  [ "${p}" ] && a[i]="${p}"
done
echo "${a[*]}"

2
하나의 라이너로 : PATH = $ (IFS = ':'; t = ($ PATH); unset IFS; t = ($ {t [@] %% * usr *}); IFS = ':'; echo "$ {티[*]}");
nicerobot

1
이 솔루션은 공백이 포함 된 PATH의 경로에서는 작동하지 않습니다. 콜론으로 대체합니다.
sschuberth

11

다음은 현재 허용 되고 가장 높은 등급의 답변 에도 불구하고 보이지 않는 문자를 PATH에 추가하지 않고 공백이 포함 된 경로에 대처할 수있는 한 줄입니다.

export PATH=$(p=$(echo $PATH | tr ":" "\n" | grep -v "/cygwin/" | tr "\n" ":"); echo ${p%:})

개인적으로도 읽기 / 이해하기 쉬우 며 awk를 사용하는 대신 일반적인 명령 만 포함합니다.


2
... 그리고 만약 당신이 파일 이름의 개행 문자에도 대처할 수있는 것을 원한다면 이것을 사용할 수 있습니다 : export PATH=$(p=$(echo $PATH | tr ":" "\0" | grep -v -z "/cygwin/" | tr "\0" ":"); echo ${p%:}) (논란의 여지가있을지라도, 당신이 이것이 필요한 이유를 스스로에게 물어보고 싶을 것입니다 :))
ehdr

이렇게하면 부분 일치가 제거되며, 이는 아마도 원하는 것이 아닐 것입니다. 내가 사용하는 것이 grep -v "^/path/to/remove\$"grep -v -x "/path/to/remove"
ShadSterling

좋은 해결책이지만 정말로 tr더 일반적 이라고 생각 awk하십니까? ;)
K.-Michael Aye

1
물론. Windows의 Git Bash와 같은 경량 환경에는 .NET과 같은 tr인터프리터가 아닌 다음 과 같은 간단한 도구가 제공됩니다 awk.
sschuberth

1
@ehdr : 당신은 교체해야 echo "..."printf "%s" "..."는 같은 경로에서 작동 할 수 -e및 유사. 참조 stackoverflow.com/a/49418406/102441
에릭

8

다음은 솔루션입니다.

  • 순수한 배쉬입니다.
  • 다른 프로세스 (예 : 'sed'또는 'awk')를 호출하지 않습니다.
  • 변경되지 않습니다 IFS.
  • 하위 쉘을 포크하지 않습니다.
  • 공백이있는 경로를 처리하고
  • 에서 인수의 모든 발생을 제거합니다 PATH.

    removeFromPath () {
       로컬 pd
       p = ": $ 1 :"
       d = ": $ PATH :"
       d = $ {d // $ p / :}
       d = $ {d / # : /}
       경로 = $ {d / % : /}
    }

4
이 솔루션이 마음에 듭니다. 변수 이름을 더 설명 적으로 만들 수 있습니까?
Anukool 2015-06-11

아주 좋아. 그러나 별표 (*)가 포함 된 경로 세그먼트에는 작동하지 않습니다. 때때로 그들은 우연히 거기에 도착합니다.
Jörg

6

function __path_remove () {
local D = ": $ {PATH} :";
[ "$ {D / : $ 1 : / :}"! = "$ D"] && PATH = "$ {D / : $ 1 : / :}";
PATH = "$ {PATH / # : /}";
export PATH = "$ {PATH / % : /}";
}

내 .bashrc 파일에서 파 내십시오. PATH를 가지고 놀다가 길을 잃으면 awk / sed / grep을 사용할 수 없게됩니다. :-)


1
그것은 아주 좋은 지적입니다. (나는 이와 같은 단순한 일을 위해 외부 유틸리티를 실행하는 것을 결코 좋아하지 않았습니다).

6

지금까지 찾은 최고의 순수 bash 옵션은 다음과 같습니다.

function path_remove {
  PATH=${PATH/":$1"/} # delete any instances in the middle or at the end
  PATH=${PATH/"$1:"/} # delete any instances at the beginning
}

이것은 기반으로 꽤 정답이를 이미 없으면 $ PATH에 디렉토리를 추가 수퍼 유저에 이상.


이것도 꽤 좋습니다. 나는 그것을 테스트했다. PATH에 중복 경로 (예 : 정확히 동일한 두 개)가 있으면 그중 하나만 제거됩니다. 한 removePath () { PATH=${PATH/":$1"/}; PATH=${PATH/"$1:"/}; }

이 솔루션 $PATH은에 대상 (즉, 삭제 될) 경로의 하위 폴더가 포함 된 경우 실패 합니다. 예 : a:abc/def/bin:b-> a/bin:b, abc/def삭제시기.
Robin Hsu

5

1991 년 이후로 bash 배포판의 함수를 사용하고 있습니다. 이것들은 여전히 ​​Fedora의 bash-docs 패키지에 있으며에서 사용 /etc/profile되었지만 더 이상은 없습니다.

$ rpm -ql bash-doc |grep pathfunc
/usr/share/doc/bash-4.2.20/examples/functions/pathfuncs
$ cat $(!!)
cat $(rpm -ql bash-doc |grep pathfunc)
#From: "Simon J. Gerraty" <sjg@zen.void.oz.au>
#Message-Id: <199510091130.VAA01188@zen.void.oz.au>
#Subject: Re: a shell idea?
#Date: Mon, 09 Oct 1995 21:30:20 +1000


# NAME:
#       add_path.sh - add dir to path
#
# DESCRIPTION:
#       These functions originated in /etc/profile and ksh.kshrc, but
#       are more useful in a separate file.
#
# SEE ALSO:
#       /etc/profile
#
# AUTHOR:
#       Simon J. Gerraty <sjg@zen.void.oz.au>

#       @(#)Copyright (c) 1991 Simon J. Gerraty
#
#       This file is provided in the hope that it will
#       be of use.  There is absolutely NO WARRANTY.
#       Permission to copy, redistribute or otherwise
#       use this file is hereby granted provided that
#       the above copyright notice and this notice are
#       left intact.

# is $1 missing from $2 (or PATH) ?
no_path() {
        eval "case :\$${2-PATH}: in *:$1:*) return 1;; *) return 0;; esac"
}
# if $1 exists and is not in path, append it
add_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="\$${2:-PATH}:$1"
}
# if $1 exists and is not in path, prepend it
pre_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="$1:\$${2:-PATH}"
}
# if $1 is in path, remove it
del_path () {
  no_path $* || eval ${2:-PATH}=`eval echo :'$'${2:-PATH}: |
    sed -e "s;:$1:;:;g" -e "s;^:;;" -e "s;:\$;;"`
}

4

나는 이것에 대한 답을 여기에 썼다 (awk도 사용). 하지만 그게 당신이 찾고있는 것인지 잘 모르겠어요? 한 줄에 맞추려고하는 대신 적어도 그것이 무엇을하는지 분명하게 보입니다. 그러나 간단한 한 라이너의 경우 물건 만 제거하는 것이 좋습니다.

echo $PATH | tr ':' '\n' | awk '$0 != "/bin"' | paste -sd:

교체는

echo $PATH | tr ':' '\n' | 
    awk '$0 != "/bin"; $0 == "/bin" { print "/bar" }' | paste -sd:

또는 (짧지 만 가독성이 낮음)

echo $PATH | tr ':' '\n' | awk '$0 == "/bin" { print "/bar"; next } 1' | paste -sd:

어쨌든, 같은 질문과 많은 유용한 답변에 대해서는 여기를 참조 하십시오 .


그리고 부분 문자열을 포함하는 줄을 제거하려면 awk '$0 !~ "/bin"'. 즉, awk 연산자로 '/ bin'을 포함하지 않는 행을 유지합니다 !~.
thoni56

3

음, bash에서는 정규 표현식을 지원하므로 간단히 다음을 수행합니다.

PATH=${PATH/:\/home\/user\/bin/}

정규 표현식이 아닌 경로 이름 확장일까요?
dreamlax

2
bash는 정규식을 지원하지만 (bash 3부터) 이것은 예제가 아니며 변수 대체입니다.
Robert Gamble

4
이것은 패턴 변수 확장이며 솔루션에는 몇 가지 문제가 있습니다. 1) 첫 번째 요소와 일치하지 않습니다. 2) "/ home / user / bin"뿐만 아니라 "/ home / user / bin"으로 시작하는 모든 항목과 일치합니다. 3) 특수 문자를 이스케이프해야합니다. 기껏해야 이것이 불완전한 예라고 말하고 싶습니다.
nicerobot

2

@BenBlank의 원래 질문에 대한 업데이트에 표시된 세 가지 기능이 마음에 듭니다. 이를 일반화하기 위해 2 인수 형식을 사용하여 PATH 또는 원하는 다른 환경 변수를 설정할 수 있습니다.

path_append ()  { path_remove $1 $2; export $1="${!1}:$2"; }
path_prepend () { path_remove $1 $2; export $1="$2:${!1}"; }
path_remove ()  { export $1="`echo -n ${!1} | awk -v RS=: -v ORS=: '$1 != "'$2'"' | sed 's/:$//'`"; }

사용 예 :

path_prepend PATH /usr/local/bin
path_append PERL5LIB "$DEVELOPMENT_HOME/p5/src/perlmods"

또한 공백이 포함 된 경로 이름을 올바르게 처리 할 수 ​​있도록 몇 가지 인용 부호를 추가했습니다.


2

Bash의 $ PATH 변수에서 경로를 제거하는 가장 우아한 방법은 무엇입니까?

awk보다 더 우아한 것은 무엇입니까?

path_remove ()  { export PATH=`echo -n $PATH | awk -v RS=: -v ORS=: '$0 != "'$1'"' | sed 's/:$//'`; 

파이썬! 더 읽기 쉽고 유지 관리가 쉬운 솔루션이며 실제로 원하는 작업을 수행하고 있는지 확인하기가 쉽습니다.

첫 번째 경로 요소를 제거하고 싶으십니까?

PATH="$(echo "$PATH" | python -c "import sys; path = sys.stdin.read().split(':'); del path[0]; print(':'.join(path))")"

(대신에서 배관 echo, os.getenv['PATH']조금 짧은 것, 그리고 위와 동일한 결과를 제공하지만, 당신이 걱정하는 환경에서 직접 파이프에 아마 가장 그래서 난, 파이썬은 그 환경 변수에 뭔가를 할 수 있다는 걱정 .)

끝에서 제거하려면 :

PATH="$(echo "$PATH" | python -c "import sys; path = sys.stdin.read().split(':'); del path[-1]; print(':'.join(path))")"

예를 들어 이러한 재사용 가능한 쉘 함수를 만들려면 .bashrc 파일에 고정하십시오.

strip_path_first () {
    PATH="$(echo "$PATH" | 
    python -c "import sys; path = sys.stdin.read().split(':'); del path[0]; print(':'.join(path))")"
}

strip_path_last () {
    PATH="$(echo "$PATH" | 
    python -c "import sys; path = sys.stdin.read().split(':'); del path[-1]; print(':'.join(path))")"
}

1

예, 예를 들어 PATH 끝에 콜론을 넣으면 경로를 제거하는 것이 조금 덜 어색하고 오류가 발생하기 쉽습니다.

path_remove ()  { 
   declare i newPATH
   newPATH="${PATH}:"
   for ((i=1; i<=${#@}; i++ )); do
      #echo ${@:${i}:1}
      newPATH="${newPATH//${@:${i}:1}:/}" 
   done
   export PATH="${newPATH%:}" 
   return 0; 
} 

path_remove_all ()  {
   declare i newPATH
   shopt -s extglob
   newPATH="${PATH}:"
   for ((i=1; i<=${#@}; i++ )); do
      newPATH="${newPATH//+(${@:${i}:1})*([^:]):/}" 
      #newPATH="${newPATH//+(${@:${i}:1})*([^:])+(:)/}" 
   done
   shopt -u extglob 
   export PATH="${newPATH%:}" 
   return 0 
} 

path_remove /opt/local/bin /usr/local/bin

path_remove_all /opt/local /usr/local 

1

$ PATH에서 중복 을 제거하는 것이 염려되는 경우 가장 우아한 방법 인 IMHO는 처음부터 추가하지 않는 것입니다. 한 줄 :

if ! $( echo "$PATH" | tr ":" "\n" | grep -qx "$folder" ) ; then PATH=$PATH:$folder ; fi

$ folder는 무엇이든 바꿀 수 있으며 공백을 포함 할 수 있습니다 ( "/ home / user / my documents").


1

지금까지 찾은 가장 우아한 순수 bash 솔루션 :

pathrm () {                                                                      
  local IFS=':'                                                                  
  local newpath                                                                  
  local dir                                                                      
  local pathvar=${2:-PATH}                                                       
  for dir in ${!pathvar} ; do                                                    
    if [ "$dir" != "$1" ] ; then                                                 
      newpath=${newpath:+$newpath:}$dir                                          
    fi                                                                           
  done                                                                           
  export $pathvar="$newpath"                                                        
}

pathprepend () {                                                                 
  pathrm $1 $2                                                                   
  local pathvar=${2:-PATH}                                                       
  export $pathvar="$1${!pathvar:+:${!pathvar}}"                                  
}

pathappend () {                                                                    
  pathrm $1 $2                                                                   
  local pathvar=${2:-PATH}                                                       
  export $pathvar="${!pathvar:+${!pathvar}:}$1"                                  
} 

1

다른 제안 된 솔루션의 대부분은 특별한 이름이 같은 포함 된 경로 세그먼트 문자열 일치에만 의존 고려하지 않는 ., ..또는를 ~. 아래의 bash 함수는 인수 및 경로 세그먼트에서 디렉토리 문자열을 확인하여 논리적 디렉토리 일치 및 문자열 일치를 찾습니다.

rm_from_path() {
  pattern="${1}"
  dir=''
  [ -d "${pattern}" ] && dir="$(cd ${pattern} && pwd)"  # resolve to absolute path

  new_path=''
  IFS0=${IFS}
  IFS=':'
  for segment in ${PATH}; do
    if [[ ${segment} == ${pattern} ]]; then             # string match
      continue
    elif [[ -n ${dir} && -d ${segment} ]]; then
      segment="$(cd ${segment} && pwd)"                 # resolve to absolute path
      if [[ ${segment} == ${dir} ]]; then               # logical directory match
        continue
      fi
    fi
    new_path="${new_path}${IFS}${segment}"
  done
  new_path="${new_path/#${IFS}/}"                       # remove leading colon, if any
  IFS=${IFS0}

  export PATH=${new_path}
}

테스트:

$ mkdir -p ~/foo/bar/baz ~/foo/bar/bif ~/foo/boo/bang
$ PATH0=${PATH}
$ PATH=~/foo/bar/baz/.././../boo/././../bar:${PATH}  # add dir with special names
$ rm_from_path ~/foo/boo/../bar/.  # remove same dir with different special names
$ [ ${PATH} == ${PATH0} ] && echo 'PASS' || echo 'FAIL'

상자 외부에 대한 플러스 하나
Martin York

1

Linux from Scratch는 다음에서 세 가지 Bash 함수를 정의합니다 /etc/profile.

# Functions to help us manage paths.  Second argument is the name of the
# path variable to be modified (default: PATH)
pathremove () {
        local IFS=':'
        local NEWPATH
        local DIR
        local PATHVARIABLE=${2:-PATH}
        for DIR in ${!PATHVARIABLE} ; do
                if [ "$DIR" != "$1" ] ; then
                  NEWPATH=${NEWPATH:+$NEWPATH:}$DIR
                fi
        done
        export $PATHVARIABLE="$NEWPATH"
}

pathprepend () {
        pathremove $1 $2
        local PATHVARIABLE=${2:-PATH}
        export $PATHVARIABLE="$1${!PATHVARIABLE:+:${!PATHVARIABLE}}"
}

pathappend () {
        pathremove $1 $2
        local PATHVARIABLE=${2:-PATH}
        export $PATHVARIABLE="${!PATHVARIABLE:+${!PATHVARIABLE}:}$1"
}

export -f pathremove pathprepend pathappend

참조 : http://www.linuxfromscratch.org/blfs/view/svn/postlfs/profile.html


1

이 질문은 모든 사람이 선호해야하는 BASH에 대해 묻는다는 것을 알고 있지만, 대칭을 좋아하고 때때로 "csh"를 사용해야하기 때문에 "path_prepend ()", "path_append ()"및 "path_remove () "위의 우아한 솔루션.

요점은 "csh"에는 함수가 없기 때문에 함수처럼 작동하는 개인적인 bin 디렉토리에 작은 쉘 스크립트를 넣었습니다. 지정된 환경 변수를 변경하기 위해 해당 스크립트를 SOURCE에 별칭을 만듭니다.

~ / bin / _path_remove.csh :

set _resolve = `eval echo $2`
setenv $1 `eval echo -n \$$1 | awk -v RS=: -v ORS=: '$1 != "'${_resolve}'"' | sed 's/:$//'`;
unset _resolve

~ / bin / _path_append.csh :

source ~/bin/_path_remove.csh $1 $2
set _base = `eval echo \$$1`
set _resolve = `eval echo $2`
setenv $1 ${_base}:${_resolve}
unset _base _resolve

~ / bin / _path_prepend.csh :

source ~/bin/_path_remove.csh $1 $2
set _base = `eval echo \$$1`
set _resolve = `eval echo $2`
setenv $1 ${_resolve}:${_base}
unset _base _resolve

~ / bin / .cshrc :


alias path_remove  "source ~/bin/_path_remove.csh  '\!:1' '\!:2'"
alias path_append  "source ~/bin/_path_append.csh  '\!:1' '\!:2'"
alias path_prepend "source ~/bin/_path_prepend.csh '\!:1' '\!:2'"

이런 식으로 사용할 수 있습니다 ...

%(csh)> path_append MODULEPATH ${HOME}/modulefiles

0

우아한 방법이 없기 때문에 이것은 상당히 문제가되는 경향이 있기 때문에 해결책을 재정렬하여 문제를 피하는 것이 좋습니다. PATH를 뜯어 내려고 시도하는 것보다 위로 빌드하십시오.

당신의 실제 문제 상황을 알고 있다면 더 구체적으로 말할 수 있습니다. 그 동안에는 소프트웨어 빌드를 컨텍스트로 사용할 것입니다.

소프트웨어 빌드의 일반적인 문제는 궁극적으로 누군가가 기본 셸 (PATH 및 기타 환경 변수)을 구성한 방식으로 인해 일부 컴퓨터에서 중단된다는 것입니다. 우아한 솔루션은 쉘 환경을 완전히 지정하여 빌드 스크립트를 면역화하는 것입니다. 빌드 스크립트를 코딩하여 컴파일러, 라이브러리, 도구, 구성 요소 등의 위치와 같이 제어하는 ​​조립 부분을 기반으로 PATH 및 기타 환경 변수를 설정합니다. 각 구성 가능한 항목을 개별적으로 설정, 확인 및 그런 다음 스크립트에서 적절하게 사용하십시오.

예를 들어, 새 고용주에서 상속받은 Maven 기반 WebLogic 대상 Java 빌드가 있습니다. 빌드 스크립트는 깨지기 쉬운 것으로 악명이 높고, 또 다른 신입 직원과 저는 3 주 (정근 시간이 아니라 여기 저기 있지만 여전히 많은 시간)를 우리 컴퓨터에서 작동하도록했습니다. 필수적인 단계는 내가 PATH를 제어하여 어떤 Java, Maven, WebLogic이 호출되고 있는지 정확히 파악하는 것입니다. 각 도구를 가리 키도록 환경 변수를 만든 다음 이러한 도구와 몇 가지 다른 도구를 기반으로 PATH를 계산했습니다. 유사한 기술이 다른 구성 가능한 설정을 길들여 드디어 재현 가능한 빌드를 만들었습니다.

그건 그렇고, Maven을 사용하지 마십시오. Java는 괜찮습니다. 클러스터링이 절대적으로 필요한 경우에만 WebLogic을 구입하십시오 (그렇지 않으면 아니요, 특히 독점 기능이 아님).

최고의 소원.


때로는 루트 액세스 권한이없고 관리자가 PATH. 물론, 당신이 직접 만들 수도 있지만, 관리자가 무언가를 움직일 때마다 그가 어디에 두 었는지 파악해야합니다. 그런 종류의 것은 관리자를 갖는 목적을 무효화합니다.
Shep

0

@litb와 마찬가지로 " 셸 스크립트에서 $ PATH 요소를 조작하는 방법 "이라는 질문에 대한 답변을 제공 했으므로 주요 답변이 여기에 있습니다.

bash및 기타 Bourne 쉘 파생물 의 '분할'기능은 $IFS필드 간 구분 기호를 사용하여 가장 깔끔하게 얻을 수 있습니다. 예를 들어, 위치 인수 (설정 $1, $2PATH 사용의 요소를, ...) :

set -- $(IFS=":"; echo "$PATH")

$ PATH에 공백이없는 한 정상적으로 작동합니다. 공백을 포함하는 경로 요소에 대해 작동하도록 만드는 것은 사소한 연습이 아닙니다. 관심있는 독자에게 맡기십시오. Perl과 같은 스크립팅 언어를 사용하여 처리하는 것이 더 간단 할 것입니다.

또한 clnpathPATH 설정에 광범위하게 사용 하는 스크립트가 있습니다 . 나는 " How to keep from PATH variable in csh " 에 대한 답변에 문서화했습니다 .


IFS = : a = ($ PATH); IFS = 분할도 좋습니다. 공백도 포함하면 작동합니다. 하지만 그런 다음 배열을 얻었고 for 루프를 사용하여 이름을 제거해야합니다.
Johannes Schaub-litb

예; 내 업데이트 된 주석과 마찬가지로이 시점에서 스크립팅 언어를 사용하는 것이 더 간단 할 것입니다.
Jonathan Leffler

0

이 문제를 성가 시게 만드는 것은 첫 번째 요소와 마지막 요소 사이의 fencepost 케이스입니다. 문제는 IFS를 변경하고 배열을 사용하여 우아하게 해결할 수 있지만 경로가 배열 형식으로 변환되면 콜론을 다시 도입하는 방법을 모르겠습니다.

다음은 $PATH문자열 조작 만 사용하는 디렉토리 하나를 제거하는 약간 덜 우아한 버전입니다 . 나는 그것을 테스트했다.

#!/bin/bash
#
#   remove_from_path dirname
#
#   removes $1 from user's $PATH

if [ $# -ne 1 ]; then
  echo "Usage: $0 pathname" 1>&2; exit 1;
fi

delendum="$1"
NEWPATH=
xxx="$IFS"
IFS=":"
for i in $PATH ; do
  IFS="$xxx"
  case "$i" in
    "$delendum") ;; # do nothing
    *) [ -z "$NEWPATH" ] && NEWPATH="$i" || NEWPATH="$NEWPATH:$i" ;;
  esac
done

PATH="$NEWPATH"
echo "$PATH"

0

다음은 Perl 한 줄짜리입니다.

PATH=`perl -e '$a=shift;$_=$ENV{PATH};s#:$a(:)|^$a:|:$a$#$1#;print' /home/usr/bin`

$a변수는 제거 할 경로를 가져옵니다. s(대체) print명령이 암시에서 작동 $_변수입니다.


0

여기 좋은 것들. 나는 처음에 속임수를 추가하지 않기 위해 이것을 사용합니다.

#!/bin/bash
#
######################################################################################
#
# Allows a list of additions to PATH with no dupes
# 
# Patch code below into your $HOME/.bashrc file or where it
# will be seen at login.
#
# Can also be made executable and run as-is.
#
######################################################################################

# add2path=($HOME/bin .)                  ## uncomment space separated list 
if [ $add2path ]; then                    ## skip if list empty or commented out
for nodup in ${add2path[*]}
do
    case $PATH in                 ## case block thanks to MIKE511
    $nodup:* | *:$nodup:* | *:$nodup ) ;;    ## if found, do nothing
    *) PATH=$PATH:$nodup          ## else, add it to end of PATH or
    esac                          ## *) PATH=$nodup:$PATH   prepend to front
done
export PATH
fi
## debug add2path
echo
echo " PATH == $PATH"
echo

1
PATH 문자열에 선행 및 후행 콜론을 추가하여 case 문을 단순화 할 수 있습니다.case ":$PATH:" in (*:"$nodup":*) ;; (*) PATH="$PATH:$nodup" ;; esac
glenn jackman

0

확장 된 글 로빙을 활성화하면 다음을 수행 할 수 있습니다.

# delete all /opt/local paths in PATH
shopt -s extglob 
printf "%s\n" "${PATH}" | tr ':' '\n' | nl
printf "%s\n" "${PATH//+(\/opt\/local\/)+([^:])?(:)/}" | tr ':' '\n' | nl 

man bash | less -p extglob

0

확장 된 글 로빙 원-라이너 (음, 일종의) :

path_remove ()  { shopt -s extglob; PATH="${PATH//+(${1})+([^:])?(:)/}"; export PATH="${PATH%:}"; shopt -u extglob; return 0; } 

$ 1에서 슬래시를 이스케이프 할 필요가없는 것 같습니다.

path_remove ()  { shopt -s extglob; declare escArg="${1//\//\\/}"; PATH="${PATH//+(${escArg})+([^:])?(:)/}"; export PATH="${PATH%:}"; shopt -u extglob; return 0; } 

0

PATH에 콜론을 추가하면 다음과 같이 할 수도 있습니다.

path_remove ()  { 
   declare i newPATH
   # put a colon at the beginning & end AND double each colon in-between
   newPATH=":${PATH//:/::}:"   
   for ((i=1; i<=${#@}; i++)); do
       #echo ${@:${i}:1}
       newPATH="${newPATH//:${@:${i}:1}:/}"   # s/:\/fullpath://g
   done
   newPATH="${newPATH//::/:}"
   newPATH="${newPATH#:}"      # remove leading colon
   newPATH="${newPATH%:}"      # remove trailing colon
   unset PATH 
   PATH="${newPATH}" 
   export PATH
   return 0 
} 


path_remove_all ()  {
   declare i newPATH extglobVar
   extglobVar=0
   # enable extended globbing if necessary
   [[ ! $(shopt -q extglob) ]]  && { shopt -s extglob; extglobVar=1; }
   newPATH=":${PATH}:"
   for ((i=1; i<=${#@}; i++ )); do
      newPATH="${newPATH//:+(${@:${i}:1})*([^:])/}"     # s/:\/path[^:]*//g
   done
   newPATH="${newPATH#:}"      # remove leading colon
   newPATH="${newPATH%:}"      # remove trailing colon
   # disable extended globbing if it was enabled in this function
   [[ $extglobVar -eq 1 ]] && shopt -u extglob
   unset PATH 
   PATH="${newPATH}" 
   export PATH
   return 0 
} 

path_remove /opt/local/bin /usr/local/bin

path_remove_all /opt/local /usr/local 

0

path_remove_all (proxxy 별) :

-newPATH="${newPATH//:+(${@:${i}:1})*([^:])/}" 
+newPATH="${newPATH//:${@:${i}:1}*([^:])/}"        # s/:\/path[^:]*//g 

0

이것은 매우 오래된 스레드이지만이 솔루션이 흥미로울 것이라고 생각했습니다.

PATH="/usr/lib/ccache:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
REMOVE="ccache" # whole or part of a path :)
export PATH=$(IFS=':';p=($PATH);unset IFS;p=(${p[@]%%$REMOVE});IFS=':';echo "${p[*]}";unset IFS)
echo $PATH # outputs /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

블로그 게시물 에서 찾았 습니다 . 이거 제일 좋아하는 것 같아요 :)


0

저는 여기에있는 대부분의 사람들과 약간 다른 접근 방식을 취했고 특별히 문자열 조작에만 집중했습니다.

path_remove () {
    if [[ ":$PATH:" == *":$1:"* ]]; then
        local dirs=":$PATH:"
        dirs=${dirs/:$1:/:}
        export PATH="$(__path_clean $dirs)"
    fi
}
__path_clean () {
    local dirs=${1%?}
    echo ${dirs#?}
}

위는 내가 사용하는 최종 기능의 간단한 예입니다. 또한 이미 PATH에 지정된 경로 앞 / 뒤에 경로를 생성 path_add_before하고 path_add_after삽입 할 수 있습니다.

dotfiles의 path_helpers.sh 에서 전체 기능 집합을 사용할 수 있습니다 . 그들은 PATH 문자열의 시작 / 중간 / 끝에서 제거 / 추가 / 앞에 / 삽입을 완전히 지원합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.