Bash'deki $ PATH değişkeninden bir yolu kaldırmanın en zarif yolu nedir?


114

Veya daha genel olarak, bir Bash ortam değişkeninde iki nokta üst üste işaretiyle ayrılmış bir listeden bir öğeyi nasıl kaldırırım?

Yıllar önce Bash değişken genişlemesinin daha gelişmiş biçimlerini kullanarak bunu yapmanın basit bir yolunu gördüğümü sanıyordum, ama eğer öyleyse, izini kaybettim. Hızlı bir Google araması, şaşırtıcı derecede az sayıda alakalı sonuç ortaya çıkardı ve benim "basit" veya "zarif" diyeceğim hiçbiri yoktu. Örneğin, sırasıyla sed ve awk kullanan iki yöntem:

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)

Açıkça hiçbir şey yok mu? Bash'de split () işlevine benzer bir şey var mı?

Güncelleme:
Kasıtlı olarak belirsiz olan sorum için özür dilemem gerekiyor gibi görünüyor; İyi bir tartışmayı kışkırtmaktansa, belirli bir kullanım durumunu çözmekle daha az ilgileniyordum. Neyse ki anladım!

Burada çok akıllıca teknikler var. Sonunda, aşağıdaki üç işlevi araç kutuma ekledim. Sihir, büyük ölçüde Martin York'un awkRS değişkenini akıllıca kullanmasına dayanan path_remove'da gerçekleşir .

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/:$//'`; }

Buradaki tek gerçek sorun, sedtakip eden iki nokta üst üste kaldırmak için kullanılmasıdır. Martin'in çözümünün geri kalanının ne kadar basit olduğunu düşünürsek, onunla yaşamaya oldukça istekliyim!


İlgili soru: Kabuk betiklerinde $ PATH öğelerini nasıl işlerim?


Herhangi bir değişken için: WORK=`echo -n ${1} | awk -v RS=: -v ORS=: '$0 != "'${3}'"' | sed 's/:$//'`; eval "export ${2}=${WORK}"ancak bunu şöyle adlandırmalısınız func $VAR VAR pattern(@ martin-york ve @ andrew-aylett'e göre)
vesperto

Yanıtlar:


51

Awk ile bir dakika:

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

Düzenleme: Aşağıdaki yorumlara yanıt verir:

$ 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!

Güvenlik sorununa yanıt olarak düzenleyin: (soruyla ilgili değil)

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

Bu ., yolunuza etkili bir şekilde eklenecek olan son girişleri silerek kalan son iki noktayı kaldırır .


1
Son öğeyi veya birden çok öğeyi kaldırmaya çalışırken başarısız olur: ilk durumda, geçerli dir (boş dize olarak; potansiyel bir güvenlik deliği), ikinci durumda ise yol öğesi olarak `` ekler.
Fred Foo

1
@larsmans: Benim için iyi çalışıyor. Not: Boş, "./" olan mevcut dizinle aynı değildir
Martin York

2
Bir "üyesi" olarak boş bir dize PATHdeğişkeni yapar , özel bir kural olarak, Hala yapar beri en az V7 Unix 1979 tüm Unix kabuklarda anlamında olabildikleri geçerli dizin bash. Kılavuzu kontrol edin veya kendiniz deneyin.
Fred Foo

1
@Martin: POSIX bu davranışı gerektirmez, ancak belgelendirir ve izin verir: pubs.opengroup.org/onlinepubs/9699919799/basedefs/…
Fred Foo

1
Bununla son öğeyi kaldırırken daha da ince bir sorun var: awk, dizenin sonuna boş bir bayt ekliyor gibi görünüyor . Yani PATH'e daha sonra başka bir dizin eklerseniz, aslında aranmayacaktır.
sschuberth

55

Kirli hack'im:

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

18
En belirgin bir çözüm için zaman iyi bir işaret asla de -otomatikleştirin süreç. :-D
Ben Blank

"Zarif" alternatiflerden çok daha güvenli.
Jortstek

44

İkame ile ilgili en büyük sorun son davalar olduğuna göre, son davaları diğer davalardan farklı kılmaya ne dersiniz? Yolun başında ve sonunda iki nokta üst üste varsa, iki nokta üst üste ile sarılı olarak istediğimiz dizgiyi arayabilirdik. Olduğu gibi, bu iki nokta üst üste kolayca ekleyip daha sonra kaldırabiliriz.

# 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

Saf bash :).


2
Biraz daha fazla buzlanma için bu öğretici bölümünü eklerim: tldp.org/LDP/abs/html/string-manipulation.html
Cyber ​​Oliveira

1
Bunu kullandım çünkü en basit çözüm gibi görünüyordu. Çok hızlı ve kolaydı ve PATH değişkenini gerçekten değiştirdiğiniz son satırdan hemen önce echo $ WORK ile çalışmanızı kolayca kontrol edebilirsiniz.
Phil Gran

2
Kesinlikle küçük bir mücevher. Bu yazıyı bulduğumda tam olarak yapmaya çalıştığım şey. -Teşekkürler Andrew! BTW: Belki ": $ PATH:" etrafına çift tırnak işareti eklemek istersiniz, boşluk içermesi ("/ usr / bin" ile aynı) ve son satır "$ WORK" olması durumunda.

2
Teşekkürler @ PacMan-- :). Eminim (yeni denedim) atamalar için boşluklara ihtiyacınız yoktur WORKve PATHdeğişken genişletme, satır değişken atama ve komut yürütme için bölümlere ayrıştırıldıktan sonra gerçekleşir. REMOVEalıntı yapılması gerekebilir veya dizginizi sabitse doğrudan yerine koyabilirsiniz.
Andrew Aylett

Dizeler korunduğunda hep kafam karıştı, bu yüzden dizeleri her zaman çift alıntı yapmaya başladım. Bunu açıkladığınız için tekrar teşekkürler. :)

26

İşte geliştirebileceğim en basit çözüm:

#!/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[*]}"

Yukarıdaki örnek, $ PATH içindeki "usr" içeren herhangi bir öğeyi kaldıracaktır. Yalnızca bu öğeyi kaldırmak için "* usr *" yerine "/ home / user / bin" yazabilirsiniz.

sschuberth başına güncelleme

A'daki boşlukların korkunç bir fikir olduğunu düşünsem $PATHde , işte bunun üstesinden gelen bir çözüm:

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[*]}");

veya

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
Tek satırlık: PATH = $ (IFS = ':'; t = ($ PATH); unset IFS; t = ($ {t [@] %% * usr *}); IFS = ':'; echo "$ {t [*]} ");
nicerobot

1
Bu çözüm, PATH içindeki boşluklar içeren yollarla çalışmaz; onları iki nokta üst üste ile değiştirir.
sschuberth

11

İşte şu anda kabul edilen ve en yüksek puan alan cevaplara rağmen, PATH'ye görünmez karakterler eklemeyen ve boşluk içeren yollarla başa çıkabilen tek satırlık bir program:

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

Şahsen ben de bunu okumayı / anlamayı kolay buluyorum ve sadece awk yerine genel komutları içeriyor.


2
... ve dosya adlarında satırsonu satırlarıyla bile baş edebilecek bir şey istiyorsanız, bunu kullanabilirsiniz: export PATH=$(p=$(echo $PATH | tr ":" "\0" | grep -v -z "/cygwin/" | tr "\0" ":"); echo ${p%:}) (tartışmalı olsa da, neden buna ihtiyacınız olduğunu kendinize sormak isteyebilirsiniz :))
ehdr

Bu, kısmi eşleşmeleri kaldıracaktır, ki bu muhtemelen istediğiniz şey değildir; grep -v "^/path/to/remove\$"Veya kullanırdımgrep -v -x "/path/to/remove"
ShadSterling

Güzel çözüm, ama gerçekten trdaha yaygın olduğunu düşünüyor awkmusunuz? ;)
K.-Michael Aye

1
Kesinlikle. Windows'ta Git Bash gibi hafif ortamlar, trbir yorumlayıcı yerine basit bir araçla birlikte gelir awk.
sschuberth

1
@ehdr: Sen değiştirmeniz gerekiyor echo "..."ile printf "%s" "..."bu gibi yollarda çalışmak için -eve benzeri. Stackoverflow.com/a/49418406/102441
Eric

8

İşte bir çözüm:

  • saf Bash,
  • diğer işlemleri çağırmaz ('sed' veya 'awk' gibi),
  • değişmez IFS,
  • bir alt kabuğu çatallamaz,
  • Yolları boşluklarla işler ve
  • içindeki argümanın tüm oluşumlarını kaldırır PATH.

    removeFromPath () {
       yerel pd
       p = ": $ 1:"
       d = ": $ YOLU:"
       d = $ {d // $ p /:}
       d = $ {d / #: /}
       YOL = $ {d /%: /}
    }

4
Bu çözümü beğendim. Değişken adlarını daha açıklayıcı yapabilir mi?
Anukool

çok hoş. Ancak yıldız işareti (*) içeren yol parçaları için çalışmaz. Bazen oraya kazara varırlar.
Jörg

6

function __path_remove () {
yerel D = ": $ {PATH}:";
["$ {D /: $ 1: /:}"! = "$ D"] && PATH = "$ {D /: $ 1: /:}";
PATH = "$ {PATH / #: /}";
dışa aktar PATH = "$ {PATH /%: /}";
}

.Bashrc dosyamdan çıkardık. PATH ile oynadığınızda ve kaybolduğunda, awk / sed / grep kullanılamaz :-)


1
Bu çok iyi bir nokta. (Böyle basit şeyler için harici yardımcı programları çalıştırmayı hiç sevmedim).

6

Şimdiye kadar bulduğum en iyi saf bash seçeneği şudur:

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
}

Bu dayanmaktadır oldukça doğru cevap için zaten orada değilse $ PATH dizin ekleyin Süper kullanıcı üzerinde bitti.


Bu da oldukça iyi. Test ettim. PATH'de yinelenen bir yol varsa (örneğin, tam olarak aynı olan iki yol), o zaman bunlardan yalnızca biri kaldırılır. Ayrıca bunu tek removePath () { PATH=${PATH/":$1"/}; PATH=${PATH/"$1:"/}; }

Bu çözüm $PATH, hedef (yani silinecek) yolun bir alt klasörünü içerdiğinde başarısız olur . Örneğin: a:abc/def/bin:b-> a/bin:b, ne zaman abc/defsilinecek.
Robin Hsu

5

Sadece 1991'den beri var olan bash dağıtımındaki işlevleri kullanıyorum. Bunlar hala Fedora'daki bash-docs paketinde ve eskiden kullanılıyordu /etc/profile, ama artık yok ...

$ 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

Bunun bir cevabı yazdım buraya (çok awk kullanarak). Ama aradığınız şeyin bu olduğundan emin değilim? Tek bir satıra sığmaya çalışmak yerine en azından bana ne yaptığı net görünüyor. Bununla birlikte, yalnızca eşyaları kaldıran basit bir tek astar için tavsiye ederim

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

Değiştiriliyor

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

veya (daha kısa ama daha az okunabilir)

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

Her neyse, aynı soru ve birçok faydalı cevap için buraya bakın .


Kısmi dizge içeren bir satırı kaldırmak isterseniz kullanın awk '$0 !~ "/bin"'. Awk işleci ile '/ bin' içermeyen satırları saklayın !~.
thoni56

3

Bash'de normal ifadeyi desteklediği için basitçe yapardım:

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

Normal ifadeler değil, sadece yol adı genişletmesi değil mi?
dreamlax

2
Bash normal ifadeleri desteklerken (bash 3 itibariyle), bu buna bir örnek değil, bu bir değişken ikamesidir.
Robert Gamble

4
Bu model değişken genişletmedir ve çözümün birkaç sorunu vardır. 1) ilk elemanla eşleşmeyecektir. 2) sadece "/ home / user / bin" ile değil, "/ home / user / bin" ile başlayan her şeyle eşleşecektir. 3) özel karakterlerden kaçış gerektirir. En iyi ihtimalle, bunun eksik bir örnek olduğunu söyleyebilirim.
nicerobot

2

@ BenBlank'in orijinal sorusuna yaptığı güncellemede gösterilen üç işlevi beğendim. Bunları genelleştirmek için 2 bağımsız değişkenli bir form kullanıyorum, bu da PATH veya istediğim başka bir ortam değişkenini ayarlamama izin veriyor:

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/:$//'`"; }

Kullanım örnekleri:

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

Boşluk içeren yol adlarının düzgün işlenmesine izin vermek için bazı tırnak işaretleri de eklediğimi unutmayın.


2

Bash'deki $ PATH değişkeninden bir yolu kaldırmanın en zarif yolu nedir?

Awk'tan daha zarif ne olabilir?

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

Python! Bu daha okunabilir ve bakımı kolay bir çözüm ve gerçekten istediğiniz şeyi yaptığını görmek için incelemek kolaydır.

İlk yol öğesini kaldırmak istediğinizi varsayalım.

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

(Borular yerine echo,os.getenv['PATH'] biraz daha kısa olurdu ve yukarıdaki aynı sonucu sağlanan, ancak önemsediğiniz ortamdan doğrudan boruya en iyisi bu yüzden, Python o ortam değişkeni ile bir şeyler yapabilir endişe değilim .)

Benzer şekilde sondan kaldırmak için:

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

Bu yeniden kullanılabilir kabuk işlevlerini yapmak için, örneğin .bashrc dosyanıza yapıştırabilirsiniz:

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

Evet, örneğin PATH'in sonuna iki nokta üst üste koymak, yolu kaldırmayı biraz daha az beceriksiz ve hataya açık hale getirir.

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 içindeki yinelenenleri kaldırmakla ilgileniyorsanız , en zarif yöntem olan IMHO, onları ilk etapta eklemek değildir. 1 satırda:

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

$ klasörü herhangi bir şeyle değiştirilebilir ve boşluk içerebilir ("/ home / kullanıcı / belgelerim")


1

Bugüne kadar bulduğum en zarif saf bash çözümü:

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

Diğer önerilen çözümlerin çoğu özel isimler gibi içeren yol parçalarını dize eşleştirme sadece güveniyor ve dikkate almazlarsa ., ..ya ~. Aşağıdaki bash işlevi, dizge eşleşmelerinin yanı sıra mantıksal dizin eşleşmelerini bulmak için bağımsız değişkenindeki ve yol bölümlerindeki dizin dizgilerini çözer.

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}
}

Ölçek:

$ 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'

artı kutunun dışında bir tane
Martin York

1

Scratch'in sunduğu Linux, içinde üç Bash işlevini tanımlar /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

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


1

Bu sorunun herkesin tercih etmesi gereken BASH hakkında sorulduğunu biliyorum, ancak simetriyi sevdiğim ve bazen "csh" kullanmam gerektiği için "path_prepend ()", "path_append ()" ve "path_remove () "yukarıdaki zarif çözüm.

İşin özü, "csh" 'ın işlevlere sahip olmamasıdır, bu nedenle kişisel bin dizinime işlevler gibi davranan küçük kabuk betikleri koyarım. Belirtilen ortam değişkeni değişikliklerini yapmak için bu komut dosyalarını SOURCE için takma adlar oluşturuyorum.

~ / 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'"

Bunları bu şekilde kullanabilirsiniz ...

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

0

Bu oldukça sorunlu olma eğiliminde olduğu için, çünkü hiçbir zarif yol YOKTUR, çözümü yeniden düzenleyerek problemden kaçınmanızı öneririm: YOLUNU yırtmaya çalışmak yerine yukarı oluşturun.

Gerçek sorun bağlamını bilseydim daha spesifik olabilirdim. Bu arada, bağlam olarak bir yazılım yapısı kullanacağım.

Yazılım yapılarıyla ilgili yaygın bir sorun, bazı makinelerde, sonuçta birinin varsayılan kabuğunu (PATH ve diğer ortam değişkenleri) nasıl yapılandırdığından dolayı kırılmasıdır. Zarif çözüm, kabuk ortamını tam olarak belirleyerek derleme komut dosyalarınızı bağışıklık kazandırmaktır. Derleyicinin konumu, kitaplıklar, araçlar, bileşenler vb. Gibi kontrol ettiğiniz parçaları birleştirmeye dayalı olarak PATH ve diğer ortam değişkenlerini ayarlamak için derleme komut dosyalarınızı kodlayın. Her bir yapılandırılabilir öğeyi ayrı ayrı ayarlayabileceğiniz, doğrulayabileceğiniz ve doğrulayabileceğiniz bir şey yapın. sonra komut dosyanızı uygun şekilde kullanın.

Örneğin, yeni işverenime devraldığım Maven tabanlı WebLogic hedefli bir Java yapısına sahibim. Oluşturma senaryosu kırılgan olmasıyla ünlüdür ve başka bir yeni çalışanla üç hafta geçirdik (tam zamanlı değil, sadece burada ve orada, ama yine de saatlerce) makinelerimizde çalışması için. Temel bir adım, PATH'nin kontrolünü ele geçirmekti, böylece hangi Java, hangi Maven ve hangi WebLogic'in çalıştırıldığını tam olarak biliyordum. Bu araçların her birine işaret etmek için ortam değişkenleri yarattım, sonra PATH'i bunlara ve birkaçına göre hesapladım. Benzer teknikler, en sonunda yeniden üretilebilir bir yapı oluşturana kadar diğer yapılandırılabilir ayarları evcilleştirdi.

Bu arada, Maven'i kullanmayın, Java sorun değil ve yalnızca kümelenmesine kesinlikle ihtiyacınız varsa WebLogic'i satın alın (aksi halde hayır, özellikle de tescilli özelliklerine değil).

En iyi dileklerimle.


bazen root erişiminiz olmaz ve yöneticiniz PATH. Elbette, kendinizinkini de oluşturabilirsiniz, ancak yöneticiniz bir şeyi her hareket ettirdiğinde, onu nereye koyduğunu bulmanız gerekir. Bu, bir yönetici sahibi olma amacını ortadan kaldırır.
Shep

0

@Litb'de olduğu gibi, " Kabuk betiklerinde $ PATH öğelerini nasıl işleyebilirim? dolayısıyla ana cevabım orada.

bashDiğer Bourne kabuk türevlerindeki 'ayırma' işlevselliği, alanlar $IFSarası ayırıcıyla en düzgün şekilde elde edilir . Örneğin, pozisyonel argümanları (ayarlamak için $1, $2YOL, kullanım elemanlarına, ...):

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

$ PATH içinde boşluk olmadığı sürece düzgün çalışacaktır. Boşluk içeren yol öğeleri için çalışmasını sağlamak, önemsiz bir alıştırmadır - ilgilenen okuyucu için bırakılmıştır. Perl gibi bir betik dili kullanarak bunun üstesinden gelmek muhtemelen daha kolaydır.

Ayrıca clnpathPATH'ımı ayarlamak için yaygın olarak kullandığım bir komut dosyam var. Bunu, " csh'de PATH değişkenini kopyalamaktan nasıl saklanır " yanıtında belgeledim .


IFS =: a = ($ PATH); IFS = bölme de güzel. boşluklar da içeriyorlarsa çalışır. ama sonra bir dizi elde edersiniz ve isimleri kaldırmak için döngülerle uğraşmanız gerekir.
Johannes Schaub - litb

Evet; Zorlaşıyor - güncellenmiş yorumumda olduğu gibi, bu noktada bir betik dili kullanmak muhtemelen daha kolay.
Jonathan Leffler

0

Bu sorunu can sıkıcı kılan, ilk ve son unsurlar arasında yer alan çit direği vakalarıdır. Sorun, IFS'yi değiştirerek ve bir dizi kullanarak zarif bir şekilde çözülebilir, ancak yol dizi biçimine dönüştürüldüğünde iki noktayı nasıl yeniden tanıtacağımı bilmiyorum.

Burada, bir dizini $PATHyalnızca dize işlemeyi kullanmaktan kaldıran biraz daha az zarif bir sürüm var . Ben test ettim.

#!/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

İşte bir Perl tek astar:

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

$aDeğişken kaldırılacak yolunu alır. s(Yedek) ve printkomutlar dolaylı faaliyet $_değişken.


0

Burada güzel şeyler. Bunu ilk etapta çift eklemekten kaçınmak için kullanıyorum.

#!/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 dizesine baştaki ve sondaki iki nokta üst üste ekleyerek durum ifadenizi basitleştirebilirsiniz:case ":$PATH:" in (*:"$nodup":*) ;; (*) PATH="$PATH:$nodup" ;; esac
glenn jackman

0

Extended globbing etkinleştirildiğinde aşağıdakileri yapmak mümkündür:

# 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

Genişletilmiş globbing tek astar (iyi, bir çeşit):

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

1 $ 'da eğik çizgilerden kaçmaya gerek yok gibi görünüyor.

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

0

PATH'e iki nokta üst üste eklemek gibi bir şey de yapabiliriz:

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'da (proxy ile):

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

0

Bu çok eski bir konu olsa da, bu çözümün ilgi çekici olabileceğini düşündüm:

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

bu blog gönderisinde buldum . Sanırım bunu en çok beğendim :)


0

Buradaki çoğu insandan biraz farklı bir yaklaşım izledim ve özellikle sadece string manipülasyonuna odaklandım, şöyle:

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#?}
}

Yukarıdakiler, kullandığım son işlevlerin basitleştirilmiş bir örneğidir. Ayrıca , PATH'de belirtilen bir yoldan önce / sonra bir yol oluşturdum path_add_beforeve path_add_aftereklemenize izin verdim .

İşlevlerin tam set mevcuttur path_helpers.sh benim de dotfiles . PATH dizesinin başına / ortasına / sonuna kaldırmayı / eklemeyi / önceden eklemeyi / eklemeyi tam olarak desteklerler.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.