Élaguer les entrées en double de la variable PATH

9

Je modifie fréquemment mon .bashrc, puis je le source. Cependant, lorsque j'ai des choses comme export PATH="~/bin:~/perl5/bin:$PATH"dans mon fichier, la PATHvariable d'environnement augmente chaque fois que je source le fichier.

Par exemple, la première fois que .bashrc est généré, la PATHvariable se compose de ~/bin:~/perl5/bin:/usr/bin:/bin.

La deuxième fois, il se compose de ~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

La troisième fois, il se compose de ~/bin:~/perl5/bin:~/bin:~/perl5/bin:~/bin:~/perl5/bin:/usr/bin:/bin.

Existe-t-il un moyen simple de ne faire qu'ajouter tout ce qui n'est pas déjà dans le CHEMIN?

Christopher Bottoms
la source

Réponses:

12

Utilisez la fonction pathmunge () disponible dans la plupart des distributions /etc/profile:

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

modifier : Pour les zshutilisateurs, typeset -U <variable_name>dédupliquera les entrées de chemin.

Sam Halicke
la source
Merci! Ce n'est pas dans /etc/profileDebian Lenny, donc je l'inclus dans mon .bashrc.
Christopher Bottoms du
Utilisation: pathmunge /some/pathaura lieu /some/pathau début $PATHet se pathmunge /some/path afterterminera /some/pathà la fin de$PATH
Christopher Bottoms
Façon plus propre de faire la vérification pour voir si le répertoire HEW existe dans le chemin courant, dans des coquilles de bash modernes: if ! [[ $PATH =~ (^|:)$1($|:) ]] ; then.
Christopher Cashell du
Je n'utiliserais pas ça. Si ma version naïve disait PATH = / foo: $ PATH, cela signifie que je veux que / foo gagne. pathmunge /foo beforene fait pas cela. Une meilleure façon serait d'ajouter / foo au début puis de supprimer les doublons par la suite.
Don Hatch
3

J'avais ce problème, j'ai donc utilisé une combinaison de techniques répertoriées sur la question StackOverflow . Voici ce que j'ai utilisé pour dédupliquer la variable PATH réelle qui avait déjà été définie, car je ne voulais pas modifier le script de 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

Vous pouvez toujours optimiser cela un peu plus, mais j'avais du code supplémentaire dans mon original pour afficher ce qui se passait pour s'assurer qu'il définissait correctement les variables. L'autre chose à noter est que j'exécute ce qui suit

  1. remplacer n'importe quel caractère ESPACE par un caractère @
  2. diviser le tableau
  3. parcourir le tableau
  4. remplacer tous les caractères @ dans la chaîne d'élément par un espace

C'est pour s'assurer que je peux gérer les répertoires avec des espaces (les répertoires de base Samba avec les noms d'utilisateurs Active Directory peuvent avoir des espaces!)

netniV
la source
ATTENTION: cette implémentation a un bug majeur !! il supprimera /bins'il y a d'autres entrées comme /usr/binparce que l'expression rationnelle correspond même si les deux entrées ne sont pas identiques!
Sukima
Voici une implémentation alternative qui corrige ce bogue: github.com/sukima/dotfiles/blob/…
Sukima
1

Définissez votre chemin explicitement.

Cakemox
la source
Merci. J'ai essayé de définir le chemin explicitement, mais j'ai un fichier .bashrc que j'utilise dans plusieurs environnements, donc la valeur par défaut exacte PATHn'est pas toujours la même.
Christopher Bottoms
1

Une seule chaîne:

for i in $(echo $PATH|tr ":" "\n"|sort|uniq);do PATH_NEW="${PATH_NEW}$i:";done;PATH="${PATH_NEW%:}"
bindbn
la source
Merci. Une difficulté que cela me causerait est qu'il réorganise alphabétiquement (ou ASCIIbétiquement) le contenu de $PATH. J'aime mettre des répertoires spécifiques au début de $PATH(comme /home/username) pour que mes copies personnelles des exécutables soient exécutées au lieu des valeurs par défaut intégrées.
Christopher Bottoms,
1

Je peux penser à deux façons différentes de résoudre ce problème. La première consiste à démarrer votre .bashrc avec une ligne qui définit explicitement votre PATH de base, de cette façon chaque fois que vous le sourcez, il est réinitialisé à la base avant d'ajouter des répertoires supplémentaires.

Par exemple, ajoutez:

# 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

Vous pouvez également rechercher un élément avant de l'ajouter au chemin d'accès. Pour ce faire, vous utiliseriez quelque chose comme:

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

Ce dernier a tendance à devenir un peu répétitif si vous ajoutez beaucoup d'entrées, cependant, j'ai donc tendance à m'en tenir au premier. Si vous vouliez l'utiliser et prévoyiez d'ajouter beaucoup d'entrées, il serait sage d'écrire une fonction bash pour la gérer.

Remarque: La deuxième option peut uniquement fonctionner comme écrit dans les versions modernes bash. La prise en charge des expressions régulières n'est pas une fonctionnalité Bourne Shell (/ bin / sh) et peut ne pas exister dans d'autres shells. En outre, l'utilisation de guillemets peut ne pas être nécessaire ou peut même provoquer des problèmes sur certaines versions les plus récentes de bash.

Christopher Cashell
la source
Merci. J'ai essayé de définir le chemin explicitement, mais j'ai un fichier .bashrc que j'utilise dans plusieurs environnements, donc la valeur par défaut exacte PATHn'est pas toujours la même.
Christopher Bottoms du
Vous pouvez toujours gérer cela en archivant le script pour voir quel est votre nom d'hôte local, puis en définissant votre chemin absolu de manière appropriée pour ce serveur.
Christopher Cashell du
Juste une faute de frappe: le! est manquant dans le if. De plus, il est étonnamment (pour moi) facile d'en faire une fonction qui boucle sur des arguments séparés par des espaces, vous pouvez donc simplement dire: add_to_PATH ~/perl5/bin ~/.bin unix.stackexchange.com/a/4973/28760 (je suppose que la casedéclaration utilisée là-bas est plus portable , mais votre ifest plus clair pour moi). EDIT étrange coïncidence que cette question ajoute également perl5!
13ren
@ 13ren: Merci pour la note. Je viens également de réaliser que j'utilisais des guillemets simples dans la ligne de définition de PATH, au lieu de guillemets doubles comme j'aurais dû. Tel qu'écrit, il aurait remplacé votre chemin existant par le nom de la variable. Oops! Apparemment, c'était une mauvaise entrée pour moi pour les fautes de frappe; les deux sont corrigés maintenant.
Christopher Cashell
0

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

Une belle doublure facile qui ne laisse pas de traces:

UN J.
la source