Jak poprawnie dodać ścieżkę do PATH?

921

Zastanawiam się, gdzie należy dodać nową ścieżkę do PATHzmiennej środowiskowej. Wiem, że można to osiągnąć przez edycję .bashrc(na przykład), ale nie jest jasne, jak to zrobić.

Tą drogą:

export PATH=~/opt/bin:$PATH

albo to?

export PATH=$PATH:~/opt/bin
Paolo
źródło
printf '\ nPATH = $ PATH: "ścieżka do dodania" \ nexport PATH \ n' >> ~ / .bashrc
Sudoer
Jeśli dodano już kilka ścieżek, np. PATH=$PATH:$HOME/.local/bin:$HOME/binMożna dodać inną, oddzielając za pomocą: np PATH=$PATH:$HOME/.local/bin:$HOME/bin:/home/ec2-user/pear/bin.
Sandeepan Nath,
2
Czy te odpowiedzi działają dla wszystkich smaków Linuksa?
Ungeheuer,

Odpowiedzi:

1033

Proste rzeczy

PATH=$PATH:~/opt/bin

lub

PATH=~/opt/bin:$PATH

w zależności od tego, czy chcesz dodać ~/opt/binna końcu (do przeszukania po wszystkich innych katalogach, w przypadku gdy program ma taką samą nazwę w wielu katalogach), czy na początku (do przeszukania przed wszystkimi innymi katalogami).

Możesz dodać wiele wpisów jednocześnie. PATH=$PATH:~/opt/bin:~/opt/node/binlub zmiany w zamówieniu działają dobrze. Nie umieszczaj exportna początku linii, ponieważ ma ona dodatkowe komplikacje (patrz poniżej w „Notatkach o powłokach innych niż bash”).

Jeśli twoja PATHkompilacja składa się z wielu różnych komponentów, możesz mieć zduplikowane wpisy. Zobacz Jak dodać ścieżkę katalogu domowego, która ma zostać wykryta przez Unix, które polecenie? i Usuń zduplikowane wpisy $ PATH za pomocą polecenia awk, aby uniknąć dodawania lub usuwania duplikatów.

Nawiasem mówiąc, niektóre dystrybucje automatycznie umieszczają ~/binW ŚCIEŻKĘ, jeśli istnieje.

Gdzie to położyć

Umieścić linię zmodyfikować PATHw ~/.profile, lub ~/.bash_profilejeśli to, co masz.

Zauważ, że ~/.bash_rcnie jest odczytywany przez żaden program i ~/.bashrcjest plikiem konfiguracyjnym interaktywnych instancji bash. Nie powinieneś definiować zmiennych środowiskowych w ~/.bashrc. Właściwym miejscem do zdefiniowania zmiennych środowiskowych, takich jak PATHis ~/.profile(lub ~/.bash_profilejeśli nie obchodzą Cię powłoki inne niż bash). Zobacz, jaka jest różnica między nimi a tym, którego powinienem użyć?

Nie wkładaj go /etc/environmentlub ~/.pam_environment: nie są to pliki powłoki, nie możesz używać podstawień jak $PATHtam. W tych plikach można jedynie zastąpić zmienną, a nie dodawać do niej.

Potencjalne komplikacje w niektórych skryptach systemowych

Nie potrzebujesz, exportjeśli zmienna jest już w środowisku: każda zmiana wartości zmiennej jest odzwierciedlana w środowisku PATH.¹ jest prawie zawsze w środowisku; wszystkie systemy uniksowe ustawiają go bardzo wcześnie (zazwyczaj w pierwszym procesie).

Podczas logowania możesz polegać na PATHtym , że jesteś już w środowisku i zawiera już niektóre katalogi systemowe. Jeśli piszesz skrypt, który może zostać wykonany wcześnie podczas konfigurowania jakiegoś środowiska wirtualnego, może być konieczne upewnienie się, że PATHnie jest pusty i wyeksportowany: jeśli PATHnadal nie jest ustawiony, to coś podobnego PATH=$PATH:/some/directoryustawi się PATHna :/some/directory, a pusty komponent na początku oznacza bieżący katalog (jak .:/some/directory).

if [ -z "${PATH-}" ]; then export PATH=/usr/local/bin:/usr/bin:/bin; fi

Uwagi na temat muszli innych niż bash

W bash, ksh i zsh, exportjest specjalna składnia, i oba, PATH=~/opt/bin:$PATHi export PATH=~/opt/bin:$PATHczynią to, co należy. W innych powłokach w stylu Bourne / POSIX, takich jak dash ( /bin/shw wielu systemach), exportjest analizowany jako zwykłe polecenie, co implikuje dwie różnice:

Tak więc w powłokach, takich jak myślnik, export PATH=~/opt/bin:$PATHustawia PATHsię na literał ciąg, ~/opt/bin/:po którym następuje wartość PATHdo pierwszej spacji. PATH=~/opt/bin:$PATH(nagie zadanie) nie wymaga cytatów i robi właściwą rzecz. Jeśli chcesz używać exportw przenośnym skrypcie, musisz napisać export PATH="$HOME/opt/bin:$PATH", lub PATH=~/opt/bin:$PATH; export PATH(lub PATH=$HOME/opt/bin:$PATH; export PATHdla przenośności nawet powłoki Bourne'a, która nie zaakceptowała export var=valuei nie zrobiła rozszerzenia tyldy).

¹ Nie było to prawdą w przypadku powłok Bourne'a (tak jak w przypadku rzeczywistej powłoki Bourne'a, a nie nowoczesnych powłok w stylu POSIX), ale w dzisiejszych czasach jest bardzo mało prawdopodobne, aby spotkać takie stare powłoki.

Gilles
źródło
Nadal nie rozumiem komplikacji związanych z eksportem. czy możesz to uprościć?
priojeet priyom
@priojeetpriyom Proste wyjaśnienie: nie potrzebujesz export.
Gilles
Dziękuję za tę odpowiedź, doskonale szczegółową. Mówisz „ Nie powinieneś definiować zmiennych środowiskowych w ~ / .bashrc ”, ale niestety 100% programów, które zainstalowałem w moim systemie, które modyfikują ścieżkę (FZF i Rust's Cargo) modyfikują ścieżkę .bashrc. Zakładam, że ponieważ FZF jest napisany również w Rust, jest zgodny ze wzorem Rust.
icc97
83

Tak czy inaczej działa, ale nie robią tego samego: elementy PATHsą sprawdzane od lewej do prawej. W pierwszym przykładzie pliki wykonywalne w ~/opt/binbędą miały pierwszeństwo przed tymi zainstalowanymi, na przykład w /usr/bin, które mogą, ale nie muszą być tym, czego chcesz.

W szczególności, z punktu widzenia bezpieczeństwa niebezpieczne jest dodawanie ścieżek z przodu, ponieważ jeśli ktoś może uzyskać dostęp do twojego zapisu ~/opt/bin, może na przykład umieścić tam inną ścieżkę ls, której prawdopodobnie użyłbyś w zamian z /bin/lsniezauważalnie. Teraz wyobraź sobie to samo, sshprzeglądarkę lub wybór ... (To samo tyczy się umieszczania. Na swojej drodze.)

Ulrich Schwarz
źródło
6
Ale jeśli chcesz mieć własną, dostosowaną wersję ls, musisz umieścić ją w katalogu przed /bin.
Barmar
16
lub alias ls = myls
waltinator,
36

Jestem zdezorientowany pytaniem 2 (ponieważ zostało usunięte z pytania, ponieważ było to spowodowane niepowiązanym problemem):

Jaki jest praktyczny sposób na dodanie większej liczby ścieżek na różnych liniach? Początkowo myślałem, że to może załatwić sprawę:

export PATH=$PATH:~/opt/bin
export PATH=$PATH:~/opt/node/bin

ale nie dzieje się tak, ponieważ drugie zadanie nie tylko się dołącza ~/opt/node/bin, ale także całe PATHwcześniej przypisane.

Jest to możliwe obejście:

export PATH=$PATH:~/opt/bin:~/opt/node/bin

ale ze względu na czytelność wolę mieć jedno zadanie dla jednej ścieżki.

Jeśli powiesz

PATH=~/opt/bin

to wszystko będzie w waszej ŚCIEŻCE. ŚCIEŻKA jest tylko zmienną środowiskową, a jeśli chcesz dodać do ŚCIEŻKI, musisz odbudować zmienną o dokładnie takiej zawartości, jakiej chcesz. To, co podajesz jako przykład pytania 2, jest dokładnie tym, co chcesz zrobić, chyba że całkowicie nie rozumiem sedna pytania.

Używam obu formularzy w moim kodzie. Mam ogólny profil, który instaluję na każdym komputerze, na którym pracuję, który wygląda tak, aby uwzględnić potencjalnie brakujące katalogi:

export PATH=/opt/bin:/usr/local/bin:/usr/contrib/bin:/bin:/usr/bin:/usr/sbin:/usr/bin/X11
# add optional items to the path
for bindir in $HOME/local/bin $HOME/bin; do
    if [ -d $bindir ]; then
        PATH=$PATH:${bindir}
    fi
done
Carl Cravens
źródło
2
Masz rację co do przykładu pytania 2, to działa. Zdezorientował mnie inny problem związany ze ŚCIEŻKĄ w moim systemie. Przepraszam za to.
Paolo,
26

Kuloodporny sposób dołączania / przygotowywania

Istnieje wiele rozważań związanych z wyborem dołączania zamiast dodawania. Wiele z nich jest objętych innymi odpowiedziami, więc nie będę ich tutaj powtarzał.

Ważną kwestią jest to, że nawet jeśli skrypty systemowe nie używają tego (zastanawiam się, dlaczego) * 1 , kuloodpornym sposobem dodania ścieżki (np. $HOME/bin) Do zmiennej środowiskowej PATH jest

PATH="${PATH:+${PATH}:}$HOME/bin"

do dołączania (zamiast PATH="$PATH:$HOME/bin") i

PATH="$HOME/bin${PATH:+:${PATH}}"

do dodawania (zamiast PATH="$HOME/bin:$PATH")

Pozwala to uniknąć fałszywego wiodącego / końcowego okrężnicy, gdy $PATHjest początkowo pusty, co może mieć niepożądane skutki uboczne i może stać się koszmarem , nieuchwytnym do znalezienia ( ta odpowiedź krótko awkopisuje przypadek ).

Objaśnienie (z rozszerzenia parametrów powłoki ):

${parameter:+word}

Jeśli parameterjest zerowy lub nieustawiony, nic nie jest podstawiane, w przeciwnym razie rozszerzenie wordjest podstawiane.

Zatem ${PATH:+${PATH}:}jest rozwinięty do: 1) nic, jeśli PATHjest zerowy lub nieustawiony, 2) ${PATH}:, jeśli PATHjest ustawiony.

Uwaga : To jest bash.


* 1 Właśnie odkryłem, że takie skrypty devtoolset-6/enablefaktycznie tego używają,

$ cat /opt/rh/devtoolset-6/enable
# General environment variables
export PATH=/opt/rh/devtoolset-6/root/usr/bin${PATH:+:${PATH}}
...
sancho.s
źródło
24

Linux określa wykonywalną ścieżkę wyszukiwania za pomocą $PATHzmiennej środowiskowej. Aby dodać katalog / dane / myscripts na początku $PATHzmiennej środowiskowej, użyj:

PATH=/data/myscripts:$PATH

Aby dodać ten katalog na końcu ścieżki, użyj następującego polecenia:

PATH=$PATH:/data/myscripts

Ale powyższe nie są wystarczające, ponieważ po ustawieniu zmiennej środowiskowej w skrypcie zmiana ta jest skuteczna tylko w skrypcie. Istnieją tylko dwa sposoby obejścia tego ograniczenia:

  • Jeśli w skrypcie wyeksportujesz zmienną środowiskową, jest ona skuteczna w programach wywoływanych przez skrypt. Zauważ, że nie działa to w programie, który wywołał skrypt.
  • Jeśli program wywołujący skrypt robi to poprzez włączenie zamiast wywołania, wszelkie zmiany środowiska w skrypcie są skuteczne w programie wywołującym. Takie włączenie można wykonać za pomocą polecenia kropki lub polecenia źródłowego.

Przykłady:

$HOME/myscript.sh
source $HOME/myscript.sh

Włączenie zasadniczo obejmuje skrypt „wywoływany” w skrypcie „wywołującym”. To jest jak #include w C. Więc działa w skrypcie lub programie „wywołującym”. Ale oczywiście nie działa w żadnych programach ani skryptach wywoływanych przez program wywołujący. Aby było skuteczne przez cały łańcuch połączeń, należy postępować zgodnie z ustawieniem zmiennej środowiskowej za pomocą polecenia eksportu.

Na przykład program powłoki bash dołącza zawartość pliku .bash_profile. Umieść następujące 2 linie w .bash_profile:

PATH=$PATH:/data/myscripts
export PATH

skutecznie umieszcza te 2 linie kodu w programie bash. Zatem w bash zmienna $ PATH zawiera $HOME/myscript.sh, a ze względu na instrukcję eksportu, wszystkie programy wywoływane przez bash mają zmienioną $PATHzmienną. Ponieważ wszystkie programy uruchamiane z wiersza polecenia bash są wywoływane przez polecenie bash, nowa ścieżka obowiązuje dla wszystkich elementów uruchamianych z wiersza polecenia bash.

Najważniejsze jest to, że aby dodać nowy katalog do ścieżki, musisz dołączyć lub dołączyć katalog do zmiennej środowiskowej $ PATH w skrypcie zawartym w powłoce i musisz wyeksportować $PATHzmienną środowiskową.

Więcej informacji tutaj

Steve Brown
źródło
19

Od pewnego czasu mam utrzymywane ze mną dwie funkcje pathaddi pathrmktóre pomagają w dodawanie elementów do ścieżki bez konieczności martwienia się o duplikacji.

pathaddpobiera argument z pojedynczą ścieżką i argument opcjonalny, afterktóry, jeśli zostanie podany, dołącza do niego, w PATHprzeciwnym razie poprzedza go.

W prawie każdej sytuacji, jeśli dodajesz do ścieżki, prawdopodobnie chcesz zastąpić wszystko, co już znajduje się na ścieżce, dlatego domyślnie wybieram prepend.

pathadd() {
    newelement=${1%/}
    if [ -d "$1" ] && ! echo $PATH | grep -E -q "(^|:)$newelement($|:)" ; then
        if [ "$2" = "after" ] ; then
            PATH="$PATH:$newelement"
        else
            PATH="$newelement:$PATH"
        fi
    fi
}

pathrm() {
    PATH="$(echo $PATH | sed -e "s;\(^\|:\)${1%/}\(:\|\$\);\1\2;g" -e 's;^:\|:$;;g' -e 's;::;:;g')"
}

Umieść je w dowolnym skrypcie, który chcesz zmienić środowisko PATH, a teraz możesz to zrobić.

pathadd "/foo/bar"
pathadd "/baz/bat" after
export PATH

Na pewno nie dodasz do ścieżki, jeśli już tam jest. Jeśli chcesz teraz upewnić się, że /baz/batjest na początku.

pathrm "/baz/bat"
pathadd "/baz/bat"
export PATH

Teraz każdą ścieżkę można przesunąć na przód, jeśli już znajduje się na ścieżce bez podwajania.

Brett Ryan
źródło
Powiązane i czystsze podejście do sprawdzania obecności katalogu w ŚCIEŻCE: unix.stackexchange.com/a/32054/135943
Wildcard
9

Nie mogę mówić o innych dystrybucjach, ale Ubuntu ma plik / etc / environment, który jest domyślną ścieżką wyszukiwania dla wszystkich użytkowników. Ponieważ mój komputer jest używany tylko przeze mnie, umieszczam tam na swojej ścieżce dowolne katalogi, chyba że jest to tymczasowy dodatek, który umieszczam w skrypcie.

Jim Bradley
źródło
6

Istnieją sytuacje, w których użycie PATH=/a/b:$PATHmoże być uznane za „niepoprawny” sposób dodania ścieżki do PATH:

  1. Dodanie ścieżki, która w rzeczywistości nie jest katalogiem.
  2. Dodanie ścieżki, która jest już PATHw tej samej formie.
  3. Dodanie ścieżki względnej (ponieważ rzeczywisty przeszukiwany katalog zmieniłby się wraz ze zmianą bieżącego katalogu roboczego).
  4. Dodanie ścieżki, która jest już w PATHinnej formie (tj. Alias ​​z powodu użycia dowiązań symbolicznych lub ..).
  5. Jeśli unikniesz robienia 4, nie przesuwaj ścieżki do przodu, PATHgdy ma to zastąpić inne wpisy w PATH.

Ta funkcja (tylko Bash) wykonuje „słuszną rzecz” w powyższych sytuacjach (z wyjątkiem, patrz poniżej), zwraca kody błędów i drukuje ładne wiadomości dla ludzi. Kody błędów i komunikaty można wyłączyć, gdy nie są potrzebne.

prepath() {
    local usage="\
Usage: prepath [-f] [-n] [-q] DIR
  -f Force dir to front of path even if already in path
  -n Nonexistent dirs do not return error status
  -q Quiet mode"

    local tofront=false errcode=1 qecho=echo
    while true; do case "$1" in
        -f)     tofront=true;       shift;;
        -n)     errcode=0;          shift;;
        -q)     qecho=':';          shift;;
        *)      break;;
    esac; done
    # Bad params always produce message and error code
    [[ -z $1 ]] && { echo 1>&2 "$usage"; return 1; }

    [[ -d $1 ]] || { $qecho 1>&2 "$1 is not a directory."; return $errcode; }
    dir="$(command cd "$1"; pwd -P)"
    if [[ :$PATH: =~ :$dir: ]]; then
        $tofront || { $qecho 1>&2 "$dir already in path."; return 0; }
        PATH="${PATH#$dir:}"        # remove if at start
        PATH="${PATH%:$dir}"        # remove if at end
        PATH="${PATH//:$dir:/:}"    # remove if in middle
    fi
    PATH="$dir:$PATH"
}

Wyjątkiem jest to, że ta funkcja nie kanonizuje ścieżek dodanych PATHza pomocą innych środków, więc jeśli znajduje się niekanoniczny alias ścieżki PATH, spowoduje to dodanie duplikatu. Próba kanonizacji ścieżek już znajdujących się w PATHjest ponurą propozycją, ponieważ ścieżka względna ma oczywiste znaczenie, gdy jest przekazywana, prepathale gdy już znajduje się na ścieżce, nie wiesz, jaki był bieżący katalog roboczy, gdy został dodany.

Curt J. Sampson
źródło
w odniesieniu do ścieżek względnych: co powiesz na posiadanie przełącznika -r, który dodawałby ścieżkę bez uczynienia jej absolutnym najpierw, i który również szukałby jej jako bezwzględnej przed dodaniem? Gdyby to był skrypt, można by go użyć w innych powłokach. Czy jest jakaś korzyść z posiadania go jako funkcji? niezły kod!
hoijui
1
@hoijui To musi być funkcja, ponieważ modyfikuje bieżące środowisko. Gdyby był to skrypt, zmodyfikowałby środowisko podprocesu uruchamiającego skrypt, a kiedy skrypt się skończył, miałbyś taki sam $PATHjak poprzednio. Jeśli chodzi o -rnie, myślę, że ścieżki względne $PATHsą po prostu zbyt zawodne i dziwne (twoja ścieżka zmienia się za każdym razem cd!), Aby chcieć wspierać coś takiego w ogólnym narzędziu.
Curt J. Sampson,
5

Dla mnie (w Mac OS X 10.9.5) dodanie nazwy ścieżki (np. /mypathname) Do pliku /etc/pathsdziałało bardzo dobrze.

Przed edycją echo $PATHzwraca:

/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin

Po edycji /etc/pathsi zrestartowaniu powłoki dołączana jest zmienna $ PATH /pathname. Rzeczywiście echo $PATHzwraca:

/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/mypathname

Stało się to, że /mypathnamezostało dołączone do $PATHzmiennej.

faelx
źródło
3
Lepiej jest dodać plik do katalogu /etc/paths.d niż edytować sam plik / etc / paths.
rbrewer
4

Aby dodać nową ścieżkę do PATHzmiennej środowiskowej:

export PATH=$PATH:/new-path/

Do tej zmiany należy zastosować do każdej muszli otwarciu, dodaj go do pliku, że powłoka będzie pozyskiwania gdy jest wywoływany. W różnych powłokach może to być:

  • Bash Shell: ~ / .bash_profile, ~ / .bashrc lub profile
  • Korn Shell: ~ / .kshrc lub .profile
  • Z Shell: ~ / .zshrc lub .zprofile

na przykład

# export PATH=$PATH:/root/learning/bin/
# source ~/.bashrc
# echo $PATH

Podaną ścieżkę można zobaczyć w powyższym wyniku.

Amit24x7
źródło
4

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 śladów :

AJ
źródło
1
-bash: awk: brak takiego pliku lub katalogu -bash: sed: brak takiego pliku lub katalogu
davidcondrey
1
@davidcondrey - awk i sed są bardzo popularnymi poleceniami zewnętrznymi. Ta odpowiedź zapewnia czysty bash sposób na osiągnięcie tego samego, więc działa nawet w przypadkach, gdy awk i / lub sed nie są obecne (lub ich odpowiednie katalogi nie znajdują się na ścieżce!)
sancho.s