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?

Niccolo M.
źródło
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.

Rozgwiazda
źródło
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)).

Niccolo M.
źródło
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
terdon
źródło
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
DavidPostill
źródło
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.

Brian Snook
źródło
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/

waltinator
źródło
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