Bardziej wygodny sposób edycji długiej ścieżki PATH?


35

Chcę dodać, w ~ / .bashrc, kilka katalogów do mojego $ PATH.

Mój $ PATH jest dość długi, więc trudno jest zobaczyć, jakie katalogi zawiera i w jakiej kolejności.

Wiem, że mogę zmodyfikować mój plik ~ / .bashrc, aby był:

PATH=$PATH:/some/dir
PATH=$PATH:/another/dir:/yet/another
PATH=$PATH:/and/another
...

ułatwiłoby to czytanie. Zastanawiałem się jednak, czy w ciągu ostatnich lat Bash nabył jakąś składnię, która ułatwia określenie długiej ścieżki. Np. Fantazjuję na temat składni podobnej do:

PATH=:((
  /some/dir
  /another/dir
  /yet/another
  /and/another
  ...
))

Wiem, że taka składnia jest nieprawidłowa. Zastanawiałem się, czy jest coś tak łatwego. Jest tu?


Tradycyjny samouczek ustawiania ścieżki PATH=foo:$PATHwydaje się niewłaściwy, ponieważ utrzymuje wzrost za każdym razem, source ~/.bashrca nawet exec bashnie może pomóc, ponieważ $PATHjest export.
— 林果 皞

Odpowiedzi:


25

Korzystam z zestawu funkcji wygody do dodawania lub dodawania ścieżki do zmiennej. Funkcje znajdują się w pakiecie dystrybucyjnym dla Basha w pliku contrib o nazwie „pathfuncs”.

  • add_path doda wpis na końcu zmiennej PATH
  • pre_path doda wpis na początku zmiennej PATH
  • del_path usunie wpis ze zmiennej PATH, gdziekolwiek jest

Jeśli podasz zmienną jako drugi argument, użyje jej zamiast ŚCIEŻKI.

Dla wygody oto:

# 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;:\$;;"`
}

Jeśli dodasz je do pliku startowego bash, możesz dodać do PATH w następujący sposób:

pre_path $HOME/bin
add_path /sbin
add_path /usr/sbin

Lub podaj inną zmienną:

pre_path $HOME/man MANPATH
pre_path $HOME/share/man MANPATH
add_path /usr/local/man MANPATH
add_path /usr/share/man MANPATH

Używam tej metody w moich plikach rc, stawiając na pierwszym miejscu ścieżki wstępne, a na końcu ścieżki dodatkowe. Sprawia, że ​​wszystkie moje zmiany na ścieżce są łatwe do zrozumienia na pierwszy rzut oka. Kolejną zaletą jest to, że wiersze są na tyle krótkie, że w razie potrzeby mogę dodać końcowy komentarz do wiersza.

Ponieważ są to funkcje, można ich używać interaktywnie z wiersza polecenia, na przykład mówiąc, add_path $(pwd)aby dodać bieżący katalog do ścieżki.


Dzięki. Sprawdziłem twój kod i działa. Co zaskakujące, znalazłem również zastosowanie del_path (A "." Wkrada się do mojej ŚCIEŻKI w niektórych sytuacjach, diabeł wie skąd, więc tak zrobiłem del_path .).
— Niccolo M.,

Cześć. możliwe jest źródło (dołączenie) tego pathfuncs ze skryptu bashrc, czy powinienem go tam skopiować / wkleić?
— Cristiano

@Cristiano Either będzie działać. To naprawdę zależy od ciebie.
— Rozgwiazda

11

OK, wymyśliłem następujące rozwiązanie, które moim zdaniem jest eleganckie (jeśli chodzi o składnię powłoki). Korzysta ze składni tablicy Basha, a także zgrabnego sposobu łączenia elementów:

paths=(
  /some/dir
  /another/dir
  '/another/dir with spaces in it'
  /yet/another
  /and/another
  /end
)
paths_joined=$( IFS=: ; echo "${paths[*]}" )

PATH=$paths_joined:$PATH

ALARM!

Okazuje się, że to rozwiązanie ma problem : w przeciwieństwie do rozwiązań @terdon i @Starfish, najpierw nie sprawdza, czy ścieżki są już w PATH. Ponieważ chcę wstawić ten kod do ~ / .bashrc (a nie do ~ / .profile), zduplikowane ścieżki wkradną się do PATH. Więc nie używaj tego rozwiązania (chyba że umieścisz go w ~ / .profile (lub, lepiej, ~ / .bash_profile, ponieważ ma on składnię specyficzną dla Bash)).


1
Bardzo dobrze. Czy możesz zaakceptować odpowiedź, aby inni nie przychodzili tutaj, aby zaoferować rozwiązanie, gdy już je znajdziesz? Dzięki
— Basic

Zduplikowane ścieżki nie są tak naprawdę problemem. Jest bardzo mało prawdopodobne, aby dodać tyle katalogów, aby PATHfaktycznie powodować problemy z wydajnością (zwłaszcza, że ​​powłoka buforuje udane wyszukiwania).
— chepner

5

Korzystam z poniższej funkcji w moim ~/.bashrc. To coś, co dostałem od sysadmina z mojego starego laboratorium, ale nie sądzę, że je napisał. Po prostu dodaj te linie do swojego ~/.profilelub ~/.bashrc:

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

Ma to różne zalety:

  • Dodawanie nowych katalogów do $PATHjest trywialne: pathmunge /foo/bar;
  • Unika duplikatów wpisów;
  • Możesz wybrać, czy dodać nowy wpis na początku ( pathmunge /foo/barczy na końcu ( pathmunge /foo/barpo)) $PATH.

Plik inicjalizacyjny powłoki zawierałby wtedy coś takiego:

pathmunge /some/dir
pathmunge /another/dir
pathmunge '/another/dir with spaces in it'
pathmunge /yet/another
pathmunge /and/another
pathmunge /end

Dzięki. Ale wybiorę rozwiązanie @ Starfish, ponieważ jego się nie pojawia grep.
— Niccolo M.,

2
@NiccoloM. nie ma problemu, zaakceptuj to, co wolisz. Uważaj jednak na podejście rozgwiazdy, wykona ona dowolny kod eval, abyś mógł spowodować poważne szkody, jeśli uruchomisz go z niewłaściwym argumentem.
— terdon

Zauważ, że szybsza funkcja istnieje w Redhat, aby to zrobić bez zewnętrznego polecenia grep, zobacz bugzilla.redhat.com/show_bug.cgi?id=544652#c7
— 林果 皞

4

Chcę dodać, w ~ / .bashrc, kilka katalogów do mojego $ PATH.

Używam następujących w Cygwin. Powinien działać w innych wersjach bash. Możesz usunąć unset PATHkompilację na swoim bieżącym PATH(jeśli to zrobisz, być może będziesz musiał dowiedzieć się, jak :poprawnie dodać separatory).

Uwaga:

  • Kiedyś miałem tę funkcję w bashfunkcji, ale straciłem ją po awarii dysku.

W moim .bash_profile:

# Build up the path using the directories in ~/.path_elements
unset PATH
while read line; do 
  PATH="${PATH}$line"; 
done < ~/.path_elements

...

# Add current directory to path
export PATH=".:${PATH}"

W ~/.path_elements:

/home/DavidPostill/bin:
/usr/local/bin:
/usr/bin:
/c/Windows/system32:
/c/Windows

Dzięki. Twoja odpowiedź zainspirowała mnie do pracy nad podobnym rozwiązaniem. (Według mojego gustu przechowywanie ścieżek w osobnym pliku jest kłopotliwe.)
— Niccolo M.,

1

Używam tego w moim .bashrc (a także w .zshrc, ponieważ zwykle używam zsh, jeśli jest dostępny zamiast bash). To prawda, że ​​wymaga ręcznego dodawania katalogów, ale zaletą jest to, że podczas aktualizacji mogę kontynuować kopiowanie na nowe serwery i nie martwić się, że PATH na nowym serwerze jest tworzony z nieistniejącymi katalogami.

##
## PATH
##
## Zamiast po prostu wrzucać naszą ŚCIEŻKĘ do katalogów, które mogą
## nie nadaje się do tego serwera, staraj się być inteligentny w kwestii tego, co dodajemy
##
ŚCIEŻKA = / usr / local / sbin: / usr / local / bin: / usr / sbin: / usr / bin: / sbin: / bin
[-d / cs / sbin] && PATH = / cs / sbin: $ PATH
[-d / cs / bin] && PATH = / cs / bin: $ PATH
[-d / usr / ucb] && PATH = $ PATH: / usr / ucb
[-d / usr / ccs / bin] && PATH = $ PATH: / usr / ccs / bin
[-d / usr / local / ssl / bin] && PATH = $ PATH: / usr / local / ssl / bin
[-d / usr / krb5 / bin] && PATH = $ PATH: / usr / krb5 / bin
[-d / usr / krb5 / sbin] && PATH = $ PATH: / usr / krb5 / sbin
[-d / usr / kerberos / sbin] && PATH = $ PATH: / usr / kerberos / sbin
[-d / usr / kerberos / bin] && PATH = $ PATH: / usr / kerberos / bin
[-d /cs/local/jdk1.5.0/bin] && PATH = $ PATH: /cs/local/jdk1.5.0/bin
[-d /usr/java/jre1.5.0_02/bin] && PATH = $ PATH: /usr/java/jre1.5.0_02/man
[-d /usr/java1.2/bin] && PATH = $ PATH: /usr/java1.2/bin
[-d /cs/local/perl5.8.0/bin] && PATH = $ PATH: /cs/local/perl5.8.0/bin
[-d / usr / perl5 / bin] && PATH = $ PATH: / usr / perl5 / bin
[-d / usr / X11R6 / bin] && PATH = $ PATH: / usr / X11R6 / bin
[-d / etc / X11] && PATH = $ PATH: / etc / X11
[-d / opt / sfw / bin] && PATH = $ PATH: / opt / sfw / bin
[-d / usr / local / apache / bin] && PATH = $ PATH: / usr / local / apache / bin
[-d / usr / apache / bin] && PATH = $ PATH: / usr / apache / bin
[-d / cs / admin / bin] && PATH = $ PATH: / cs / admin / bin
[-d / usr / openwin / bin] && PATH = $ PATH: / usr / openwin / bin
[-d / usr / xpg4 / bin] && PATH = $ PATH: / usr / xpg4 / bin
[-d / usr / dt / bin] && PATH = $ PATH: / usr / dt / bin

Robię to samo dla mojej MANPATH:

##
## MANPATH
##
## Zamiast po prostu przeszukiwać naszą MANPATH katalogami, które mogą
## nie nadaje się do tego serwera, staraj się być inteligentny w kwestii tego, co dodajemy
##
MANPATH = / usr / local / man
[-d / usr / share / man] && MANPATH = $ MANPATH: / usr / share / man
[-d / usr / local / share / man] && MANPATH = $ MANPATH: / usr / local / share / man
[-d / usr / man] && MANPATH = $ MANPATH: / usr / man
[-d / cs / man] && MANPATH = $ MANPATH: / cs / man
[-d / usr / krb5 / man] && MANPATH = $ MANPATH: / usr / krb5 / man
[-d / usr / kerberos / man] && MANPATH = $ MANPATH: / usr / kerberos / man
[-d / usr / local / ssl / man] && MANPATH = $ MANPATH: / usr / local / ssl / man
[-d /cs/local/jdk1.5.0/man] && MANPATH = $ MANPATH: /cs/local/jdk1.5.0/man
[-d /usr/java/jre1.5.0_02/man] && MANPATH = $ MANPATH: /usr/java/jre1.5.0_02/man
[-d /usr/java1.2/man] && MANPATH = $ MANPATH: /usr/java1.2/man
[-d / usr / X11R6 / man] && MANPATH = $ MANPATH: / usr / X11R6 / man
[-d / usr / local / apache / man] && MANPATH = $ MANPATH: / usr / local / apache / man
[-d / usr / local / mysql / man] && MANPATH = $ MANPATH: / usr / local / mysql / man
[-d /cs/local/perl5.8.0/man] && MANPATH = $ MANPATH: /cs/local/perl5.8.0/man
[-d / usr / perl5 / man] && MANPATH = $ MANPATH: / usr / perl5 / man
[-d / usr / local / perl / man] && MANPATH = $ MANPATH: / usr / local / perl / man
[-d /usr/local/perl5.8.0/man] && MANPATH = $ MANPATH: /usr/local/perl5.8.0/man
[-d / usr / openwin / man] && MANPATH = $ MANPATH: / usr / openwin / man

Oprócz posiadania jednego pliku, który mogę kopiować do systemów w różnych środowiskach bez obawy dodania nieistniejących katalogów do PATH, to podejście ma również tę zaletę, że pozwala mi określić kolejność, w jakiej chcę, aby katalogi pojawiały się w PATH. Ponieważ pierwszy wiersz każdej definicji całkowicie redefiniuje zmienną PATH, mogę zaktualizować plik .bashrc i pobrać go po edycji, aby zaktualizować moją powłokę bez dodawania zduplikowanych wpisów (czego doświadczyłem dawno temu, kiedy po prostu zaczynałem od „ $ PATH = $ PATH: / new / dir ". Dzięki temu otrzymam czystą kopię w żądanej kolejności.


1
sugerując alternatywę: d="/usr/share/man" ; [ -d "$d" ] && MANPATH="$MANPATH:${d}"będzie krótszy i łatwiejszy do dodania nowego katalogu (po prostu skopiuj wiersz i edytuj pierwszą część „d = ....”). Jednak w przypadku ŚCIEŻKI myślę, że skończy się zbyt dużo katalogów w ŚCIEŻCE, co nie zawsze jest dobre (co jeśli jakieś polecenie „foo” istnieje na jednej z mniej znanych ścieżek i zrobi coś zupełnie innego tego oczekiwałby zwykły użytkownik?)
— Olivier Dulac

OP poprosił o bardziej zwięzły sposób dodawania ścieżek. Jest to o wiele bardziej szczegółowe i bardziej podatne na błędy niż to, czego już próbowali uniknąć.
— underscore_d

-1

Jest prosty sposób! Przeczytaj Funkcje powłoki i zmienne ścieżki w Linux Journal , 01 marca 2000 Autor Stephen Collyer

Funkcje pozwalają mi używać nowego typu danych w moim środowisku bash - listy oddzielonych dwukropkami. Oprócz ŚCIEŻKI używam ich do dostosowywania LOCATE_PATH, MANPATH i innych oraz jako ogólny typ danych w programowaniu bash. Oto jak skonfigurowałem swoją ŚCIEŻKĘ (używając funkcji):

# Add my bin directory to $PATH at the beginning, so it overrides 
addpath -f -p PATH $HOME/bin

# For Raspberry Pi development (add at end)
addpath -b -p PATH ${HOME}/rpi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin

# remove nonexistent directories from PATH
delpath -n -p PATH

# ensure PATH contains unique entries
uniqpath -p PATH

Ponieważ link do Linux Journal jest określany jako „uszkodzony”, umieściłem Funkcje ścieżki Bash w pliku .shar na stronie http://pastebin.ubuntu.com/13299528/


3
Jakie to są funkcje? Jak wykorzystuje je PO? Przypuszczalnie są one opisane w niedziałającym łączu w twojej odpowiedzi, dlatego właśnie chcemy, aby odpowiedzi były zawsze samodzielne. Edytuj i dołącz rzeczywiste funkcje do swojej odpowiedzi.
— terdon

@terdon: link działa dla mnie, umieściłem plik .shar na pastebin, nie opublikuję tutaj 1K wierszy.
— waltinator,

Ups, teraz też działa dla mnie. Nie zrobiłem tego, kiedy zostawiłem komentarz. W każdym razie sedno mojego komentarza polegało na tym, że w odpowiedziach staramy się unikać linkowania do zasobów zewnętrznych. Chcemy, aby informacje były tutaj, gdzie mogą być aktualizowane, edytowane i odporne na gnicie linków.
— terdon
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.