Przycinaj zduplikowane wpisy ze zmiennej PATH

9

Często modyfikuję mój plik .bashrc, a następnie go pozyskuję. Jednak gdy mam takie rzeczy jak export PATH="~/bin:~/perl5/bin:$PATH"w pliku, PATHzmienna środowiskowa rośnie za każdym razem, gdy plik źródłowy.

Na przykład, przy pierwszym uruchomieniu .bashrc, PATHzmienna składa się z ~/bin:~/perl5/bin:/usr/bin:/bin.

Drugi raz składa się z ~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

Po raz trzeci składa się z ~/bin:~/perl5/bin:~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

Czy istnieje prosty sposób, aby dodawać tylko to, czego jeszcze nie ma w ŚCIEŻCE?

Christopher Bottoms
źródło

Odpowiedzi:

12

Użyj funkcji pathmunge () dostępnej w większości dystrybucji /etc/profile:

pathmunge () {
if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then
   if [ "$2" = "after" ] ; then
      PATH=$PATH:$1
   else
      PATH=$1:$PATH
   fi
fi
}

edycja : dla zshużytkowników typeset -U <variable_name>deduplikuje wpisy ścieżki.

Sam Halicke
źródło
Dzięki! Nie ma go w /etc/profileDebianie Lennym, więc włączam go do mojego .bashrc.
Christopher Bottoms
Sposób użycia: pathmunge /some/pathzostanie umieszczony /some/pathna początku $PATHi pathmunge /some/path afterzostanie umieszczony /some/pathna końcu$PATH
Christopher Bottoms
Cleaner ze sposobów sprawdzania, czy istnieje katalog HEW w bieżącej ścieżce, w nowoczesnych powłok bash: if ! [[ $PATH =~ (^|:)$1($|:) ]] ; then.
Christopher Cashell
Nie użyłbym tego. Jeśli moja naiwna wersja mówi PATH = / foo: $ PATH, oznacza to, że chcę / foo wygrać. pathmunge /foo beforenie osiąga tego. Lepszym sposobem byłoby dodanie / foo na początku, a następnie usunięcie duplikatów.
Don Hatch
3

Miałem ten problem, więc użyłem kombinacji technik wymienionych w pytaniu StackOverflow . Oto, czego użyłem do dedukcji faktycznej zmiennej PATH, która została już ustawiona, ponieważ nie chciałem modyfikować skryptu podstawowego.

    tmppath=(${PATH// /@})
    array=(${tmppath//:/ })
    for i in "${array[@]//@/ }"
    do
        if ! [[ $PATH_NEW =~ "$i" ]]; then
            PATH_NEW="${PATH_NEW}$i:";
        fi
    done;
    PATH="${PATH_NEW%:}"
    export PATH
    unset PATH_NEW

Zawsze mogłeś zoptymalizować to trochę bardziej, ale miałem w oryginale dodatkowy kod, aby wyświetlić, co się dzieje, aby upewnić się, że poprawnie ustawia zmienne. Inną rzeczą do odnotowania jest to, że wykonuję następujące czynności

  1. zamień dowolny znak SPACJI na znak @
  2. podzielić tablicę
  3. zapętlić przez tablicę
  4. zamień dowolne znaki @ w ciągu elementu spacją

Ma to na celu zapewnienie, że mogę obsługiwać katalogi ze spacjami w (katalogi domowe Samba z nazwami użytkowników Active Directory mogą mieć spacje!)

netniV
źródło
OSTRZEŻENIE: Ta implementacja ma poważny błąd !! zostanie usunięty, /binjeśli istnieją inne wpisy, np. /usr/binponieważ wyrażenie regularne pasuje, nawet jeśli dwa wpisy nie są takie same!
Sukima
Oto alternatywna implementacja, która naprawia ten błąd: github.com/sukima/dotfiles/blob/…
Sukima
1

Wyraźnie określ swoją ścieżkę.

Cakemox
źródło
Dzięki. Próbowałem jawnie ustawić ścieżkę, ale mam plik .bashrc, którego używam w wielu środowiskach, więc dokładne ustawienie domyślne PATHnie zawsze jest takie samo.
Christopher Bottoms
1

Tylko jeden ciąg:

for i in $(echo $PATH|tr ":" "\n"|sort|uniq);do PATH_NEW="${PATH_NEW}$i:";done;PATH="${PATH_NEW%:}"
bindbn
źródło
Dzięki. Trudno mi by to spowodować, że alfabetycznie (lub ASCIIbetalnie) zmienia kolejność treści $PATH. Lubię umieszczać określone katalogi na początku $PATH(lubię /home/username), aby moje osobiste kopie plików wykonywalnych były uruchamiane zamiast wbudowanych ustawień domyślnych.
Christopher Bottoms
1

Mogę wymyślić dwa różne sposoby rozwiązania tego problemu. Pierwszym z nich jest uruchomienie .bashrc z linią, która jawnie ustawia podstawową ŚCIEŻKĘ, w ​​ten sposób za każdym razem, gdy ją pobierasz, jest ona resetowana do bazy przed dodaniem dodatkowych katalogów.

Na przykład dodaj:

# Reset the PATH to prevent duplication and to make sure that we include
# everything we want.
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Alternatywnie możesz sprawdzić element przed dodaniem go do ścieżki. Aby to zrobić, użyłbyś czegoś takiego:

if ! [[ $PATH =~ '~/perl5/bin' ]]
then
    PATH="~/perl5/bin:$PATH"
fi

To ostatnie robi się jednak trochę powtarzalne, jeśli dodajesz dużo wpisów, więc staram się trzymać tego pierwszego. Jeśli chcesz tego użyć i planujesz dodać wiele wpisów, dobrze byłoby napisać funkcję bash, aby sobie z tym poradzić.

Uwaga: druga opcja może działać tylko tak, jak napisano w nowoczesnych wersjach bash. Obsługa wyrażeń regularnych nie jest funkcją Bourne Shell (/ bin / sh) i może nie istnieć w innych powłokach. Również użycie cudzysłowów może nie być potrzebne, a nawet powodować problemy w niektórych najnowszych wersjach bash.

Christopher Cashell
źródło
Dzięki. Próbowałem jawnie ustawić ścieżkę, ale mam plik .bashrc, którego używam w wielu środowiskach, więc dokładne ustawienie domyślne PATHnie zawsze jest takie samo.
Christopher Bottoms
Nadal możesz sobie z tym poradzić, sprawdzając w skrypcie nazwę lokalnego hosta, a następnie ustawiając ścieżkę bezwzględną odpowiednio dla tego serwera.
Christopher Cashell
Tylko literówka:! brakuje w if. Co zaskakujące (dla mnie) łatwo jest przekształcić to w funkcję, która add_to_PATH ~/perl5/bin ~/.bin zapętla argumenty oddzielone spacjami, więc możesz po prostu powiedzieć: unix.stackexchange.com/a/4973/28760 (Myślę, że caseużyte tam wyrażenie jest bardziej przenośne , ale twój ifjest dla mnie jaśniejszy). EDYTUJ dziwny zbieg okoliczności, że to pytanie dodaje również perl5!
13ren
@ 13ren: Dzięki za notatkę. Właśnie zdałem sobie sprawę, że użyłem pojedynczych cudzysłowów w wierszu zestawu PATH, zamiast podwójnych cudzysłowów, jak powinienem. Jak napisano, zastąpiłoby to istniejącą ścieżkę nazwą zmiennej. Ups! Najwyraźniej był to zły wpis dla literówek; oba są teraz naprawione.
Christopher Cashell
0

Oto moje rozwiązanie: PATH=$(echo -n $PATH | awk -v RS=: -v ORS=: '!x[$0]++' | sed "s/\(.*\).\{1\}/\1/")

Przyjemna, prosta wkładka, która nie pozostawia śladu:

AJ
źródło