Dodaj katalog do $ PATH, jeśli jeszcze go nie ma


126

Czy ktoś napisał funkcję bash, aby dodać katalog do $ PATH tylko wtedy, gdy jeszcze go nie ma?

Zazwyczaj dodaję do PATH używając czegoś takiego:

export PATH=/usr/local/mysql/bin:$PATH

Jeśli zbuduję ŚCIEŻKĘ w .bash_profile, to nie zostanie odczytana, chyba że sesja, w której jestem, to sesja logowania - co nie zawsze jest prawdą. Jeśli zbuduję moją ŚCIEŻKĘ w .bashrc, to działa ona z każdą podpowłoką. Jeśli więc uruchomię okno terminala, a następnie uruchomię ekran, a następnie uruchom skrypt powłoki, otrzymuję:

$ echo $PATH
/usr/local/mysql/bin:/usr/local/mysql/bin:/usr/local/mysql/bin:....

Spróbuję zbudować funkcję bash o nazwie, add_to_path()która dodaje katalog tylko wtedy, gdy go nie ma. Ale jeśli ktoś już napisał (lub znalazł) coś takiego, nie spędzę na tym czasu.


Zobacz stackoverflow.com/questions/273909/..., aby uzyskać informacje na temat infrastruktury, która może pomóc.
— dmckee,


Jeśli określisz problem jako „tylko dodawanie, jeśli jeszcze go nie ma”, będziesz niegrzecznie zaskoczony, gdy nadejdzie dzień, w którym ważne jest, aby wstawiony element był na początku, ale tam się nie kończy. Lepszym rozwiązaniem byłoby wstawienie elementu, a następnie usunięcie duplikatów, więc jeśli nowy wpis już tam był, zostanie skutecznie przeniesiony na początek.
— Don Hatch

Odpowiedzi:


125

Z mojego .bashrc:

pathadd() {
    if [ -d "$1" ] && [[ ":$PATH:" != *":$1:"* ]]; then
        PATH="${PATH:+"$PATH:"}$1"
    fi
}

Pamiętaj, że PATH powinien być już oznaczony jako wyeksportowany, więc ponowny eksport nie jest potrzebny. To sprawdza, czy katalog istnieje i jest katalogiem przed dodaniem go, co może Cię nie obchodzić.

Ponadto dodaje to nowy katalog na końcu ścieżki; na początek, użyj PATH="$1${PATH:+":$PATH"}"zamiast powyższej PATH=linii.


26
Zależy mi.
— Dennis Williamson,

4
@Neil: To działa, ponieważ porównuje ze ":$PATH:"zamiast po prostu"$PATH"
— Gordon Davisson

3
@GordonDavisson: przepraszam, mój test był zły i masz rację.
— Neil,

2
@GordonDavisson Jaki jest sens rzeczy w nawiasach klamrowych. Wydaje mi się, że nie potrafię go rozwiązać „ ${PATH:+"$PATH:"}$ 1”
— dekoder łodzi

5
@ Mark0978: Właśnie to zrobiłem, aby rozwiązać problem wskazany przez bukzor. ${variable:+value}oznacza sprawdzenie, czy variablejest zdefiniowane i ma niepustą wartość, a jeśli tak, daje wynik oceny value. Zasadniczo, jeśli PATH jest niepuste na początku, ustawia ją na "$PATH:$1"; jeśli jest pusty, ustawia go tylko "$1"(zauważ brak dwukropka).
— Gordon Davisson,

19

Rozwijając odpowiedź Gordona Davissona, obsługuje wiele argumentów

pathappend() {
  for ARG in "$@"
  do
    if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]; then
        PATH="${PATH:+"$PATH:"}$ARG"
    fi
  done
}

Więc możesz zrobić pathappend path1 path2 path3 ...

Do przygotowania

pathprepend() {
  for ((i=$#; i>0; i--)); 
  do
    ARG=${!i}
    if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]; then
        PATH="$ARG${PATH:+":$PATH"}"
    fi
  done
}

Możesz to zrobić podobnie do pathappend

ścieżkaprepend ścieżka1 ścieżka2 ścieżka3 ...


3
To jest świetne! Dokonałem jednej małej zmiany. W przypadku funkcji „pathprepend” wygodnie jest czytać argumenty w odwrotnej kolejności, dzięki czemu można na przykład powiedzieć pathprepend P1 P2 P3i skończyć z PATH=P1:P2:P3. Aby uzyskać takie zachowanie, zmień for ARG in "$@" donafor ((i=$#; i>0; i--)); do ARG=${!i}
— ishmael

Dzięki @ishmael, dobra sugestia, zredagowałem odpowiedź. Zdaję sobie sprawę, że twój komentarz ma więcej niż dwa lata, ale od tamtej pory nie wróciłem. Muszę wymyślić, jak zdobyć e-maile z wymianą stosów, aby wylądowały w mojej skrzynce odbiorczej!
— Guillaume Perrault-Archambault,

14

Oto moja odpowiedź na to pytanie w połączeniu ze strukturą funkcji Douga Harrisa. Używa wyrażeń regularnych Bash:

add_to_path ()
{
    if [[ "$PATH" =~ (^|:)"${1}"(:|$) ]]
    then
        return 0
    fi
    export PATH=${1}:$PATH
}

Działa to dla mnie tylko przy użyciu $1zamiast${1}
— Andrei

@Andrei: Tak, nawiasy klamrowe są w tym przypadku niepotrzebne. Nie jestem pewien, dlaczego je zawarłem.
— Dennis Williamson,

10

Umieść to w komentarzach do wybranej odpowiedzi, ale wydaje się, że komentarze nie obsługują formatowania PRE, więc dodaj odpowiedź tutaj:

@ Gordon-Davisson Nie jestem wielkim fanem niepotrzebnych cytatów i konkatenacji. Zakładając, że używasz wersji bash> = 3, możesz zamiast tego użyć wbudowanych wyrażeń regularnych bash i wykonać:

pathadd() {
    if [ -d "$1" ] && [[ ! $PATH =~ (^|:)$1(:|$) ]]; then
        PATH+=:$1
    fi
}

To poprawnie obsługuje przypadki, w których w katalogu lub ŚCIEŻCE są spacje. Jest pewne pytanie, czy wbudowany silnik regex bash jest wystarczająco wolny, aby to mogło być mniej wydajne niż konkatenacja i interpolacja łańcuchów w porównaniu z twoją wersją, ale jakoś wydaje mi się bardziej czysta estetycznie.


1
Komentarze obsługują formatting using the backticktylko, ale nie masz żadnej przyzwoitej kontroli akapitu.
— Boatcoder

To stawia dodatek na końcu. Często pożądane jest dodanie na początku, aby zastąpić istniejące lokalizacje.
— Dennis Williamson,

@DennisWilliamson To uczciwa kwestia, choć nie zalecałbym tego jako zachowania domyślnego. Nietrudno wymyślić, jak zmienić na prepend.
— Christopher Smith

@ChristopherSmith - re: unnecessary quotingoznacza, że ​​wiesz z góry, że $PATHnie jest zerowa. "$PATH"sprawia, że ​​OK jest, czy ŚCIEŻKA ma wartość zerową, czy nie. Podobnie jeśli $1zawiera znaki, które mogą pomylić parser poleceń. Użycie wyrażenia regularnego w cudzysłowie "(^|:)$1(:|$)"zapobiega temu.
— Jesse Chisholm,

@JesseChisholm: W rzeczywistości uważam, że Christopher ma na myśli to, że reguły różnią się między [[i ]]. Wolę cytować wszystko, co może ewentualnie trzeba być cytowane, chyba że powoduje to się nie powiedzie, ale wierzę, że ma rację i że cytuje naprawdę nie są potrzebne wokół $PATH. Z drugiej strony wydaje mi się, że masz rację $1.
— Scott

7
idempotent_path_prepend ()
{
    PATH=${PATH//":$1"/} #delete any instances in the middle or at the end
    PATH=${PATH//"$1:"/} #delete any instances at the beginning
    export PATH="$1:$PATH" #prepend to beginning
}

Gdy potrzebujesz $ HOME / bin, aby pojawił się dokładnie raz na początku ścieżki $ PATH i nigdzie indziej, nie akceptuj substytutów.


Dzięki, to miłe, eleganckie rozwiązanie, ale stwierdziłem, że musiałem zrobić PATH=${PATH/"... zamiast PATH=${PATH//"... aby to zadziałało.
— Mark Booth

Formularz podwójnego ukośnika powinien pasować do dowolnej liczby dopasowań; pojedynczy ukośnik pasuje tylko do pierwszego (wyszukaj „Podstawienie wzorca” na stronie podręcznika bash). Nie jestem pewien, dlaczego to nie zadziałało ...
— andybuckley

Nie udaje się to w nietypowym przypadku, który $1jest jedynym wpisem (bez dwukropków). Wpis zostaje podwojony.
— Dennis Williamson,

Usuwa również zbyt agresywnie, jak wskazał PeterS6g .
— Dennis Williamson,

6

Oto alternatywne rozwiązanie, które ma dodatkową zaletę polegającą na usuwaniu zbędnych wpisów:

function pathadd {
    PATH=:$PATH
    PATH=$1${PATH//:$1/}
}

Pojedynczy argument tej funkcji jest dodawany do PATH, a pierwsza instancja tego samego ciągu jest usuwana z istniejącej ścieżki. Innymi słowy, jeśli katalog już istnieje na ścieżce, jest on promowany na przód, a nie dodawany jako duplikat.

Funkcja działa, przygotowując dwukropek do ścieżki, aby upewnić się, że wszystkie wpisy mają dwukropek z przodu, a następnie przygotowując nowy wpis do istniejącej ścieżki z usuniętym wpisem. Ostatnia część jest wykonywana przy użyciu ${var//pattern/sub}notacji bash ; więcej szczegółów znajdziesz w instrukcji bash .


Dobra myśl; wadliwe wdrożenie. Zastanów się, co się dzieje, jeśli już /home/robertw twojej PATHa wami pathadd /home/rob.
— Scott,

5

Oto moja (wydaje mi się, że została napisana lata temu przez Oscara, administratora mojego starego laboratorium, wszystko mu się należy), była w moim baszkrze od wieków. Ma tę dodatkową zaletę, że pozwala na dodanie lub dodanie nowego katalogu według potrzeb:

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

Stosowanie:

$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin
$ pathmunge /bin/
$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin
$ pathmunge /sbin/ after
$ echo $PATH
/bin/:/usr/local/bin/:/usr/bin:/sbin/

5

Jeśli chodzi o dodawanie, podoba mi się rozwiązanie @ Russella, ale jest mały błąd: jeśli spróbujesz dodać coś takiego jak „/ bin” do ścieżki „/ sbin: / usr / bin: / var / usr / bin: / usr / local / bin: / usr / sbin „zastępuje” / bin: „3 razy (kiedy tak naprawdę w ogóle nie pasował). Łącząc naprawę tego z dołączającym rozwiązaniem @ gordon-davisson, otrzymuję to:

path_prepend() {
    if [ -d "$1" ]; then
        PATH=${PATH//":$1:"/:} #delete all instances in the middle
        PATH=${PATH/%":$1"/} #delete any instance at the end
        PATH=${PATH/#"$1:"/} #delete any instance at the beginning
        PATH="$1${PATH:+":$PATH"}" #prepend $1 or if $PATH is empty set to $1
    fi
}

4

Prosty alias taki jak ten poniżej powinien załatwić sprawę:

alias checkInPath="echo $PATH | tr ':' '\n' | grep -x -c "

Wystarczy podzielić ścieżkę na znak: i porównać każdy składnik z argumentem, który przekazujesz. Grep sprawdza pełne dopasowanie linii i wypisuje liczbę.

Przykładowe użycie:

$ checkInPath "/usr/local"
1
$ checkInPath "/usr/local/sbin"
1
$ checkInPath "/usr/local/sbin2"
0
$ checkInPath "/usr/local/" > /dev/null && echo "Yes" || echo "No"
No
$ checkInPath "/usr/local/bin" > /dev/null && echo "Yes" || echo "No"
Yes
$ checkInPath "/usr/local/sbin" > /dev/null && echo "Yes" || echo "No"
Yes
$ checkInPath "/usr/local/sbin2" > /dev/null && echo "Yes" || echo "No"
No

Zamień polecenie echo na addToPath lub podobny alias / funkcję.


Używanie „grep -x” jest prawdopodobnie szybsze niż pętla, którą umieściłem w funkcji bash.
— Doug Harris,


2

Oto co przygotowałem:

add_to_path ()
{
    path_list=`echo $PATH | tr ':' ' '`
    new_dir=$1
    for d in $path_list
    do
        if [ $d == $new_dir ]
        then
            return 0
        fi
    done
    export PATH=$new_dir:$PATH
}

Teraz w .bashrc mam:

add_to_path /usr/local/mysql/bin

Zaktualizowana wersja po komentarzu na temat tego, jak mój oryginał nie będzie obsługiwał katalogów ze spacjami (dzięki temu pytaniu za wskazanie mnie do używania IFS):

add_to_path ()
{
    new_dir=$1
    local IFS=:
    for d in $PATH
    do
        if [[ "$d" == "$new_dir" ]]
        then
            return 0
        fi
    done
    export PATH=$new_dir:$PATH
}

1
To może zawieść jeśli nazwa katalogu już PATHzawiera spacje, *, ?albo [... ].
— Scott

Dobra uwaga ... ale jestem oldschoolowym facetem z Linuksem i nigdy nie użyłbym ścieżki z białymi znakami :-)
— Doug Harris

Dobrze dla ciebie, ponieważ nie tworzysz rzeczy z białymi spacjami w ich nazwach. Wstydź się za pisanie kodu, który nie poradzi sobie z nimi, jeśli istnieją. I co ma z tym wspólnego bycie „oldschoolowym facetem od Linuksa”? Windoze mógł spopularyzować ten pomysł (dziękuję Documents and Settingsi Program Files), ale Unix wspierał ścieżki zawierające białe spacje jeszcze zanim istniało Windoze.
— Scott,

2

Jestem trochę zaskoczony, że nikt jeszcze o tym nie wspominał, ale możesz użyć go readlink -fdo konwersji ścieżek względnych na ścieżki bezwzględne i dodania ich do ŚCIEŻKI jako takiej.

Na przykład, aby poprawić odpowiedź Guillaume Perrault-Archambault,

pathappend() {
  for ARG in "$@"
  do
    if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]; then
        PATH="${PATH:+"$PATH:"}$ARG"
    fi
  done
}

staje się

pathappend() {
    for ARG in "$@"
    do
        if [ -d "$ARG" ] && [[ ":$PATH:" != *":$ARG:"* ]]
        then
            if ARGA=$(readlink -f "$ARG")               #notice me
            then
                if [ -d "$ARGA" ] && [[ ":$PATH:" != *":$ARGA:"* ]]
                then
                    PATH="${PATH:+"$PATH:"}$ARGA"
                fi
            else
                PATH="${PATH:+"$PATH:"}$ARG"
            fi
        fi
    done
}

1. Podstawy - co to robi?

readlink -fKomenda (między innymi) przekonwertować ścieżkę względną do ścieżki bezwzględnej. To pozwala ci zrobić coś takiego

$ cd / path / to / my / bin / dir
$ pathappend .
$ echo „$ PATH”
<twoja_ stara_ścieżka> : / path / to / my / bin / dir

2. Dlaczego dwa razy testujemy obecność w ŚCIEŻCE?

Rozważmy powyższy przykład. Jeśli użytkownik powie z katalogu po raz drugi, będzie . Oczywiście nie będzie obecny w . Ale wtedy zostanie ustawiony na (bezwzględny ekwiwalent ścieżki ), który już jest w . Musimy więc unikać dodawania do drugiego razu.pathappend ./path/to/my/bin/dirARG..PATHARGA/path/to/my/bin/dir.PATH/path/to/my/bin/dirPATH

Być może, co ważniejsze, głównym celem readlinkjest, jak sama nazwa wskazuje, przyjrzenie się dowiązaniu symbolicznemu i odczytanie zawartej w nim ścieżki (tj. Wskazuje). Na przykład:

$ ls -ld /usr/lib/perl/5.14
-rwxrwxrwx  1   root   root    Sep  3  2015 /usr/lib/perl/5.14 -> 5.14.2
$ readlink /usr/lib/perl/5.14
5.14.2
$ readlink -f /usr/lib/perl/5.14
/usr/lib/perl/5.14.2

Otóż, jeśli powiecie pathappend /usr/lib/perl/5.14, a macie już /usr/lib/perl/5.14ŚCIEŻKĘ, to w porządku; możemy po prostu zostawić to tak, jak jest. Ale jeśli /usr/lib/perl/5.14nie ma go już w ŚCIEŻCE, wywołujemy readlinki otrzymujemy ARGA= /usr/lib/perl/5.14.2, a następnie dodajemy to do PATH. Ale poczekaj chwilę - jeśli już powiedziałeś pathappend /usr/lib/perl/5.14, to masz już /usr/lib/perl/5.14.2ŚCIEŻKĘ i ponownie musimy to sprawdzić, aby uniknąć dodania go PATHpo raz drugi.

3. O co chodzi if ARGA=$(readlink -f "$ARG")?

W przypadku niejasności linia ta sprawdza, czy się readlinkpowiedzie. To tylko dobra, defensywna praktyka programowania. Jeśli zamierzamy wykorzystać dane wyjściowe polecenia  m jako część polecenia  n (gdzie m  <  n ), rozsądnie jest sprawdzić, czy polecenie  m nie powiodło się i jakoś sobie z tym poradzić. Nie sądzę, że prawdopodobnie readlinkzawiedzie - ale, jak omówiono w Jak odzyskać bezwzględną ścieżkę dowolnego pliku z OS X i innych, readlinkjest wynalazkiem GNU. Nie jest on określony w POSIX, więc jego dostępność w Mac OS, Solaris i innych systemach uniksowych innych niż Linux jest wątpliwa. (W rzeczywistości właśnie przeczytałem komentarz, który mówi „readlink -fwydaje się, że nie działa w systemie Mac OS X 10.11.6, ale realpathdziała po wyjęciu z pudełka. ”Jeśli więc korzystasz z systemu, który go nie ma readlinklub gdzie readlink -fnie działa, możesz to zmodyfikować skrypt do użycia realpath.) Instalując siatkę bezpieczeństwa, zwiększamy przenośność naszego kodu.

Oczywiście, jeśli korzystasz z systemu, który nie ma readlink(lub  realpath), nie będziesz chciał tego robić .pathappend .

Drugi -dtest ( [ -d "$ARGA" ]) jest prawdopodobnie prawdopodobnie niepotrzebny. Nie mogę wymyślić żadnego scenariusza, w którym $ARGkatalog jest readlinkudany, ale  $ARGAnie jest katalogiem. Właśnie skopiowałem i wkleiłem pierwsze ifzdanie, aby utworzyć trzecie, i zostawiłem  -dtest bez lenistwa.

4. Wszelkie inne komentarze?

Tak. Podobnie jak wiele innych odpowiedzi tutaj, ta sprawdza, czy każdy argument jest katalogiem, przetwarza go, jeśli jest, i ignoruje go, jeśli nie jest. Może to (lub nie musi) być odpowiednie, jeśli używasz pathappend tylko .plików „ ” (takich jak .bash_profilei .bashrc) i innych skryptów. Ale, jak pokazała ta odpowiedź (powyżej), użycie jej interaktywnie jest całkowicie wykonalne. Będziesz bardzo zdziwiony, jeśli to zrobisz

$ pathappend /usr/local/nysql/bin
$ mysql
-bash: mysql: command not found

Czy zauważyłeś, że powiedziałem nysql w pathappendpoleceniu, a nie mysql? I to pathappendnic nie powiedział; po prostu cicho zignorował niewłaściwy argument?

Jak powiedziałem powyżej, dobrą praktyką jest obsługa błędów. Oto przykład:

pathappend() {
    for ARG in "$@"
    do
        if [ -d "$ARG" ]
        then
            if [[ ":$PATH:" != *":$ARG:"* ]]
            then
                if ARGA=$(readlink -f "$ARG")           #notice me
                then
                    if [[ ":$PATH:" != *":$ARGA:"* ]]
                    then
                        PATH="${PATH:+"$PATH:"}$ARGA"
                    fi
                else
                    PATH="${PATH:+"$PATH:"}$ARG"
                fi
            fi
        else
            printf "Error: %s is not a directory.\n" "$ARG" >&2
        fi
    done
}

(1) Należy dodać cytaty: readlink -f "$ARG". (2) Nie wiem, dlaczego tak się stało (szczególnie po pomyślnym zakończeniu -d "$ARG"testu), ale możesz chcieć sprawdzić, czy się readlinknie powiódł. (3) Wydaje się, że pomijasz readlinkpodstawową funkcję - mapowanie symbolicznej nazwy linku na „prawdziwą nazwę pliku”. Jeśli (na przykład) /binjest dowiązaniem symbolicznym /bin64, powtarzane wywołania do pathappend /binmogą spowodować powiedzenie ŚCIEŻKI …:/bin64:/bin64:/bin64:/bin64:…. Prawdopodobnie powinieneś (również) sprawdzić, czy nowa wartość $ARGjest już w PATH.
— Scott,

(1) Dobra obserwacja, naprawiłem to. (2) w którym przypadku readlink nie powiedzie się? Zakładając, że ścieżka jest poprawna i odnosi się do prawidłowej lokalizacji. (3) Nie jestem pewien, co decyduje o podstawowej funkcji readlink, uważam, że większość (jeśli nie wszystkie?) Ścieżek w systemie plików unix można podzielić na 2 typy łączy, dowiązania symboliczne i dowiązania twarde. Jeśli chodzi o wpis zduplikowanej ścieżki, masz rację, ale celem mojej odpowiedzi nie było dodanie tego (jak zauważyłem, że inne odpowiedzi już o tym wspominały). Jedynym powodem Dodaję kolejny odpowiedź jest przyczynienie się coś, czego myśl nie była już przyczynił
— qwertyzw

(2) Mam na myśli, że jeśli przynajmniej nazwa polecenia sugeruje / sugeruje jego cel, wówczas „link” w readlink może odnosić się do linków twardych lub miękkich. Masz jednak rację: man readlink mówi „readlink - drukuj rozstrzygnięte dowiązania symboliczne lub kanoniczne nazwy plików”, .w moim przykładzie uważam, że można go uznać za kanoniczną nazwę pliku. Poprawny?
— qwertyzw

(1) Dla osób, które rozumieją dowiązania symboliczne, readlinknazwa wyraźnie wskazuje na ich cel - patrzy na dowiązanie symboliczne i odczytuje zawartą w nim nazwę ścieżki (tzn. Wskazuje na). (2) Powiedziałem , że nie wiem, dlaczego readlinkmiałbym zawieść. Zwracałem ogólną uwagę na to, że jeśli skrypt lub funkcja zawiera wiele poleceń, a polecenie  n ma katastrofalną awarię (lub w ogóle nie ma sensu), jeśli polecenie  m nie powiedzie się (gdzie m  <  n ), rozsądną dobrą praktyką jest sprawdź, czy polecenie m nie powiodło się, i jakoś sobie z tym poradzić - przynajmniej… (ciąg dalszy)
— Scott

(Ciąg dalszy)… przerwij skrypt / funkcję za pomocą diagnostyki. Hipotetycznie sytuacja readlinkmoże się nie powieść, jeśli (a) nazwa katalogu zostanie zmieniona lub usunięta (przez inny proces) między Twoimi połączeniami do testi readlinklub (b) jeśli /usr/bin/readlinkzostanie usunięty (lub uszkodzony). (3) Wygląda na to, że brakuje ci mojego punktu. Nie zachęcam do powielania innych odpowiedzi; Mówię, że sprawdzając, czy oryginał ARG(z wiersza poleceń) jest już w środku PATH, ale nie powtarzając sprawdzania nowego ARG(wyjścia z readlink), twoja odpowiedź jest niepełna, a zatem nieprawidłowa. … (Ciąg dalszy)
— Scott,

1
function __path_add(){  
if [ -d "$1" ] ; then  
    local D=":${PATH}:";   
    [ "${D/:$1:/:}" == "$D" ] && PATH="$PATH:$1";  
    PATH="${PATH/#:/}";  
    export PATH="${PATH/%:/}";  
fi  
}  

1

Ten sposób działa dobrze:

if [[ ":$PATH:" != *":/new-directory:"* ]]; then PATH=${PATH}:/new-directory; fi

0

Moje wersje mniej ostrożnie podchodzą do pustych ścieżek i nalegają, aby ścieżki były prawidłowe i katalogi, niż niektóre zamieszczone tutaj, ale znajduję dużą kolekcję prepend / append / clean / unique-ify / itp. funkcje powłoki, które będą przydatne do manipulacji ścieżkami. Cała partia, w obecnym stanie, jest tutaj: http://pastebin.com/xS9sgQsX (opinie i ulepszenia bardzo mile widziane!)


0

Możesz użyć perl one liner:

appendPaths() { # append a group of paths together, leaving out redundancies
    # use as: export PATH="$(appendPaths "$PATH" "dir1" "dir2")
    # start at the end:
    #  - join all arguments with :,
    #  - split the result on :,
    #  - pick out non-empty elements which haven't been seen and which are directories,
    #  - join with :,
    #  - print
    perl -le 'print join ":", grep /\w/ && !$seen{$_}++ && -d $_, split ":", join ":", @ARGV;' "$@"
}

Oto w bash:

addToPath() { 
    # inspired by Gordon Davisson, http://superuser.com/a/39995/208059
    # call as: addToPath dir1 dir2
    while (( "$#" > 0 )); do
    echo "Adding $1 to PATH."
    if [[ ! -d "$1" ]]; then
        echo "$1 is not a directory.";
    elif [[ ":$PATH:" == *":$1:"* ]]; then
        echo "$1 is already in the path."
    else
            export PATH="${PATH:+"$PATH:"}$1" # ${x:-defaultIfEmpty} ${x:+valueIfNotEmpty}
    fi
    shift
    done
}

0

Lekko zmodyfikowałem odpowiedź Gordona Davissona, aby użyć aktualnego katalogu, jeśli nie został podany. Możesz więc po prostu zrobić paddz katalogu, który chcesz dodać do ŚCIEŻKI.

padd() {
  current=`pwd`
  p=${1:-$current}
  if [ -d "$p" ] && [[ ":$PATH:" != *":$p:"* ]]; then
      PATH="$p:$PATH"
  fi
}

0

Możesz sprawdzić, czy zmienna niestandardowa została ustawiona, w przeciwnym razie ustaw ją, a następnie dodaj nowe wpisy:

if [ "$MYPATHS" != "true" ]; then
    export MYPATHS="true"
    export PATH="$PATH:$HOME/bin:"

    # java stuff
    export JAVA_HOME="$(/usr/libexec/java_home)"
    export M2_HOME="$HOME/Applications/apache-maven-3.3.9"
    export PATH="$JAVA_HOME/bin:$M2_HOME/bin:$PATH"

    # etc...
fi

Oczywiście wpisy te mogą być nadal duplikowane, jeśli zostaną dodane przez inny skrypt, na przykład /etc/profile.


0

Ten skrypt umożliwia dodanie na końcu $PATH:

PATH=path2; add_to_PATH after path1 path2:path3
echo $PATH
path2:path1:path3

Lub dodaj na początku $PATH:

PATH=path2; add_to_PATH before path1 path2:path3
echo $PATH
path1:path3:path2

# Add directories to $PATH iff they're not already there
# Append directories to $PATH by default
# Based on https://unix.stackexchange.com/a/4973/143394
# and https://unix.stackexchange.com/a/217629/143394
add_to_PATH () {
  local prepend  # Prepend to path if set
  local prefix   # Temporary prepended path
  local IFS      # Avoid restoring for added laziness

  case $1 in
    after)  shift;; # Default is to append
    before) prepend=true; shift;;
  esac

  for arg; do
    IFS=: # Split argument by path separator
    for dir in $arg; do
      # Canonicalise symbolic links
      dir=$({ cd -- "$dir" && { pwd -P || pwd; } } 2>/dev/null)
      if [ -z "$dir" ]; then continue; fi  # Skip non-existent directory
      case ":$PATH:" in
        *":$dir:"*) :;; # skip - already present
        *) if [ "$prepend" ]; then
           # ${prefix:+$prefix:} will expand to "" if $prefix is empty to avoid
           # starting with a ":".  Expansion is "$prefix:" if non-empty.
            prefix=${prefix+$prefix:}$dir
          else
            PATH=$PATH:$dir  # Append by default
          fi;;
      esac
    done
  done
  [ "$prepend" ] && PATH=$prefix:$PATH
}

0

Oto sposób zgodny z POSIX.

# USAGE: path_add [include|prepend|append] "dir1" "dir2" ...
#   prepend: add/move to beginning
#   append:  add/move to end
#   include: add to end of PATH if not already included [default]
#          that is, don't change position if already in PATH
# RETURNS:
# prepend:  dir2:dir1:OLD_PATH
# append:   OLD_PATH:dir1:dir2
# If called with no paramters, returns PATH with duplicate directories removed
path_add() {
    # use subshell to create "local" variables
    PATH="$(path_unique)"
    export PATH="$(path_add_do "$@")"
}

path_add_do() {
    case "$1" in
    'include'|'prepend'|'append') action="$1"; shift ;;
    *)                            action='include'   ;;
    esac

    path=":$PATH:" # pad to ensure full path is matched later

    for dir in "$@"; do
        #       [ -d "$dir" ] || continue # skip non-directory params

        left="${path%:$dir:*}" # remove last occurrence to end

        if [ "$path" = "$left" ]; then
            # PATH doesn't contain $dir
            [ "$action" = 'include' ] && action='append'
            right=''
        else
            right=":${path#$left:$dir:}" # remove start to last occurrence
        fi

        # construct path with $dir added
        case "$action" in
            'prepend') path=":$dir$left$right" ;;
            'append')  path="$left$right$dir:" ;;
        esac
    done

    # strip ':' pads
    path="${path#:}"
    path="${path%:}"

    # return
    printf '%s' "$path"
}

# USAGE: path_unique [path]
# path - a colon delimited list. Defaults to $PATH is not specified.
# RETURNS: `path` with duplicated directories removed
path_unique() {
    in_path=${1:-$PATH}
    path=':'

    # Wrap the while loop in '{}' to be able to access the updated `path variable
    # as the `while` loop is run in a subshell due to the piping to it.
    # https://stackoverflow.com/questions/4667509/shell-variables-set-inside-while-loop-not-visible-outside-of-it
    printf '%s\n' "$in_path" \
    | /bin/tr -s ':' '\n'    \
    | {
            while read -r dir; do
                left="${path%:$dir:*}" # remove last occurrence to end
                if [ "$path" = "$left" ]; then
                    # PATH doesn't contain $dir
                    path="$path$dir:"
                fi
            done
            # strip ':' pads
            path="${path#:}"
            path="${path%:}"
            # return
            printf '%s\n' "$path"
        }
}

Jest żłobienia z Guillaume Perrault-Archambault „s odpowiedź na to pytanie i mike511 ” s odpowiedź tutaj .

AKTUALIZACJA 23.11.2017: Naprawiono błąd na @Scott


Zamierzałem głosować za udostępnieniem opcji wiersza polecenia do wyboru między wstępnym a późniejszym (dołączaniem), z domyślnym ustawieniem. Ale potem pomyślałem: to okropnie dużo nieco tajemniczego kodu bez wyjaśnienia. (A fakt, że masz dwie funkcje, z których jedna zmienia PATH i odzwierciedla jej nową wartość, a druga przechwytuje dane wyjściowe i przypisuje je ponownie do PATH , jest po prostu niepotrzebną złożonością.)… (Ciąg dalszy)
— Scott

(Ciąg dalszy)… A potem zauważyłem, że linki były nieprawidłowe. (I nie jestem pewien, dlaczego obwiniasz tych facetów; chyba nie skopiowałeś zbyt wiele z ich odpowiedzi.) I wtedy zauważyłem, że kod był zły. Wydaje mi się, że dobrze działa z utrzymaniem dobrze uformowanej ŚCIEŻKI, ale nie ma gwarancji, że jest ona już dobrze uformowana, szczególnie jeśli masz nieoświeconą /etc/profile. Katalog, który próbujesz dodać do PATH, może już tam być więcej niż jeden raz , a twój kod dusi się. … (Ciąg dalszy)
— Scott,

(Ciąg dalszy)… Na przykład, jeśli spróbujesz /a/cksię przygotować /b:/a/ck:/tr:/a/ck, otrzymasz /a/ck:/b:/a/ck:/tr:/tr:/a/ck.
— Scott,
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.