Elimina le voci duplicate dalla variabile PATH


9

Modifico frequentemente il mio .bashrc e quindi lo fonte. Tuttavia, quando ho cose come export PATH="~/bin:~/perl5/bin:$PATH"nel mio file, allora la PATHvariabile d'ambiente cresce ogni volta che sorgente il file.

Ad esempio, la prima volta che .bashrc viene fornito, la PATHvariabile è composta da ~/bin:~/perl5/bin:/usr/bin:/bin.

La seconda volta è composta da ~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

La terza volta è composta ~/bin:~/perl5/bin:~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

C'è un modo semplice per farlo aggiungere solo tutto ciò che non è già nel PERCORSO?

Risposte:


12

Usa la funzione pathmunge () disponibile nella maggior parte delle distro /etc/profile:

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

modifica : per gli zshutenti, typeset -U <variable_name>verranno deduplicate le voci del percorso.


Grazie! Non è /etc/profilesu Debian Lenny, quindi lo includo nel mio .bashrc.
Christopher Bottoms,

Uso: pathmunge /some/pathmetterà /some/patha inizio $PATHe pathmunge /some/path aftermetterà /some/patha fine$PATH
Christopher Bottoms

Modo più pulito di fare il controllo per vedere se la directory HEW esista nel percorso corrente, nelle moderne shell bash: if ! [[ $PATH =~ (^|:)$1($|:) ]] ; then.
Christopher Cashell,

Non lo userei. Se la mia versione ingenua diceva PATH = / foo: $ PATH significa che voglio / foo per vincere. pathmunge /foo beforenon ci riesce. Un modo migliore sarebbe quello di aggiungere / foo all'inizio e quindi rimuovere i duplicati in seguito.
Don Hatch,

3

Ho riscontrato questo problema, quindi ho usato una combinazione di tecniche elencate nella domanda StackOverflow . Quello che segue è quello che ho usato per deduplicare la variabile PATH effettiva che era già stata impostata, poiché non volevo modificare lo script di base.

    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

Potresti sempre ottimizzarlo un po 'di più, ma nel mio originale avevo un codice aggiuntivo per mostrare cosa stava succedendo per assicurarmi che stesse impostando correttamente le variabili. L'altra cosa da notare è che eseguo quanto segue

  1. sostituire qualsiasi carattere SPACE con un carattere @
  2. dividere l'array
  3. scorrere attraverso l'array
  4. sostituire eventuali caratteri @ nella stringa dell'elemento con uno spazio

Questo per assicurarmi di poter gestire le directory con spazi (le home directory di Samba con nomi utente di Active Directory possono contenere spazi!)


ATTENZIONE: questa implementazione ha un grosso bug !! rimuoverà /binse ci sono altre voci come /usr/binperché la regexp corrisponde anche se le due voci non sono uguali!
Sukima,

Ecco un'implementazione alternativa che corregge quel bug: github.com/sukima/dotfiles/blob/…
Sukima

1

Imposta il tuo percorso esplicitamente.


Grazie. Ho provato a impostare esplicitamente il percorso, ma ho un file .bashrc che utilizzo in più ambienti, quindi il valore predefinito esatto PATHnon è sempre lo stesso.
Christopher Bottoms,

1

Una sola stringa:

for i in $(echo $PATH|tr ":" "\n"|sort|uniq);do PATH_NEW="${PATH_NEW}$i:";done;PATH="${PATH_NEW%:}"

Grazie. Una cosa che difficilmente mi causerebbe è che riordina alfabeticamente (o ASCIIbeticamente) il contenuto di $PATH. Mi piace mettere directory specifiche all'inizio di $PATH(like /home/username) in modo che le mie copie personali degli eseguibili vengano eseguite invece delle impostazioni predefinite integrate.
Christopher Bottoms,

1

Posso pensare a due modi diversi in cui potresti risolverlo. Il primo è quello di avviare il tuo .bashrc con una linea che imposta esplicitamente il tuo PATH di base, in questo modo ogni volta che lo procedi, viene resettato alla base prima di aggiungere ulteriori directory.

Ad esempio, aggiungi:

# 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

In alternativa, è possibile verificare la presenza di un elemento prima di aggiungerlo al percorso. Per fare ciò, dovresti usare qualcosa come:

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

Quest'ultimo tende a diventare un po 'ripetitivo se aggiungi molte voci, quindi tendo a rimanere fedele al primo. Se si desidera utilizzarlo e si prevede di aggiungere molte voci, scrivere una funzione bash per gestirlo sarebbe saggio.

Nota: la seconda opzione può funzionare solo come scritto nelle versioni moderne bash. Il supporto per le espressioni regolari non è una funzionalità Bourne Shell (/ bin / sh) e potrebbe non esistere in altre shell. Inoltre, l'uso delle virgolette potrebbe non essere necessario o potrebbe persino causare problemi con alcune versioni più recenti di bash.


Grazie. Ho provato a impostare esplicitamente il percorso, ma ho un file .bashrc che utilizzo in più ambienti, quindi il valore predefinito esatto PATHnon è sempre lo stesso.
Christopher Bottoms,

Puoi ancora gestirlo controllando lo script per vedere qual è il tuo nome host locale e quindi impostando il percorso assoluto in modo appropriato per quel server.
Christopher Cashell,

Solo un errore di battitura: il! manca nel file if. Inoltre, è sorprendentemente (per me) facile trasformarlo in una funzione che scorre su argomenti separati da spazio, quindi puoi semplicemente dire: add_to_PATH ~/perl5/bin ~/.bin unix.stackexchange.com/a/4973/28760 (immagino che l' caseaffermazione usata lì sia più portatile , ma il tuo ifè più chiaro per me). EDIT strana coincidenza che quella domanda aggiunge anche perl5!
13

@ 13ren: Grazie per la nota. Mi sono anche reso conto di aver usato virgolette singole nella riga del set PATH, invece di virgolette doppie come avrei dovuto. Come scritto, avrebbe sostituito il percorso esistente con il nome della variabile. Oops! Apparentemente quella era una cattiva voce per me per i refusi; entrambi sono stati risolti ora.
Christopher Cashell,

0

Ecco la mia soluzione: PATH=$(echo -n $PATH | awk -v RS=: -v ORS=: '!x[$0]++' | sed "s/\(.*\).\{1\}/\1/")

Una bella fodera semplice che non lascia tracce:

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.