Question Ajouter le répertoire à $ PATH s'il n'est pas déjà présent


Quelqu'un a-t-il écrit une fonction bash pour ajouter un répertoire à $ PATH seulement si ce n'est déjà fait?

J'ajoute généralement à PATH en utilisant quelque chose comme:

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

Si je construis mon PATH dans .bash_profile, alors il n'est pas lu à moins que la session que je suis soit une session de connexion - ce qui n'est pas toujours vrai. Si je construis mon PATH dans .bashrc, alors il s'exécute avec chaque sous-shell. Donc, si je lance une fenêtre de terminal et que je lance ensuite un script shell, j'obtiens:

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

Je vais essayer de construire une fonction bash appelée add_to_path() qui ajoute uniquement le répertoire s'il n'y est pas. Mais si quelqu'un a déjà écrit (ou trouvé) une telle chose, je ne passerai pas le temps dessus.


116
2017-09-11 16:19


origine


Voir stackoverflow.com/questions/273909/... pour certaines infrastructures qui peuvent aider. - dmckee
unix.stackexchange.com/questions/4965/ - Ciro Santilli 新疆改造中心 六四事件 法轮功
Si vous définissez le problème comme "n’ajoutant que si ce n’est pas déjà fait", vous allez être grossièrement surpris lorsque le jour viendra où il est important que l’élément inséré soit au début, mais que cela ne se termine pas là. Une meilleure approche serait d'insérer l'élément, puis de supprimer les doublons. Par conséquent, si la nouvelle entrée était déjà présente, elle serait effectivement déplacée au début. - Don Hatch


Réponses:


De mon .bashrc:

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

Notez que PATH doit déjà être marqué comme exporté, donc la réexportation n'est pas nécessaire. Cela vérifie si le répertoire existe et est un répertoire avant de l'ajouter, ce qui ne vous intéresse pas.

En outre, cela ajoute le nouveau répertoire à la fin du chemin; mettre au début, utiliser PATH="$1${PATH:+":$PATH"}" au lieu de ce qui précède PATH= ligne.


116
2017-09-12 03:08



Je m'inquiète. - Dennis Williamson
@Neil: Cela fonctionne, car il se compare à ":$PATH:" au lieu de juste "$PATH" - Gordon Davisson
@GordonDavisson: Je m'excuse, mon test était faux et vous avez raison. - Neil
@GordonDavisson Quel est le but des choses dans les accolades. Je n'arrive pas à le comprendre "${PATH:+"$PATH:"}1 $ " - boatcoder
@ Mark0978: C'est ce que j'ai fait pour résoudre le problème signalé par bukzor. ${variable:+value} signifie vérifier si variable est défini et a une valeur non vide, et si c'est le cas, donne le résultat de l'évaluation value. Fondamentalement, si PATH est non vierge pour commencer, il le place à "$PATH:$1"; s'il est vide, il le met juste "$1" (notez l'absence de deux points). - Gordon Davisson


En développant la réponse de Gordon Davisson, cela soutient plusieurs arguments

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

Donc, vous pouvez faire pathappend path1 path2 path3 ...

Pour faire précéder,

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

Semblable à pathappend, vous pouvez faire

pathprepend path1 path2 path3 ...


19
2018-05-15 18:42



C'est bien! J'ai fait un petit changement. Pour la fonction 'pathprepend', il est utile de lire les arguments en sens inverse, pour pouvoir dire, par exemple, pathprepend P1 P2 P3 et se retrouver avec PATH=P1:P2:P3. Pour obtenir ce comportement, changez for ARG in "$@" do à for ((i=$#; i>0; i--)); do ARG=${!i} - ishmael
Merci @ishmael, bonne suggestion, j'ai édité la réponse. Je me rends compte que votre commentaire a plus de deux ans, mais je ne suis pas revenu depuis. Je dois trouver comment faire en sorte que les courriels d'échange de piles arrivent dans ma boîte de réception! - Guillaume Perrault-Archambault


Voici quelque chose de ma réponse à cette question combiné avec la structure de la fonction de Doug Harris. Il utilise les expressions régulières Bash:

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

12
2017-09-11 19:11



Cela a fonctionné pour moi seulement en utilisant $1 au lieu de ${1} - Andrei
@Andrei: Oui, les accolades sont inutiles dans ce cas. Je ne sais pas pourquoi je les ai inclus. - Dennis Williamson


Mettez ceci dans les commentaires à la réponse sélectionnée, mais les commentaires ne semblent pas soutenir le formatage PRE, ajoutant ainsi la réponse ici:

@ gordon-davisson Je ne suis pas un grand fan de citations inutiles et de concaténation. En supposant que vous utilisez une version bash> = 3, vous pouvez utiliser les expressions rationnelles intégrées à bash et faire:

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

Cela gère correctement les cas où il y a des espaces dans le répertoire ou le PATH. On se demande si le moteur de regex intégré à bash est suffisamment lent pour que cela soit moins efficace que la concaténation de chaînes et l'interpolation de votre version, mais cela me semble plus esthétique.


10
2018-03-02 18:20



Support des commentaires formatting using the backtick seulement mais vous ne recevez aucun contrôle de paragraphe décent. - boatcoder
Cela ajoute l'addition à la fin. Il est souvent souhaitable d'ajouter au début afin de remplacer les emplacements existants. - Dennis Williamson
@ DennisWilliamson C'est un bon point, même si je ne recommande pas cela comme comportement par défaut. Il n'est pas difficile de savoir comment changer pour un ajout préalable. - Christopher Smith
@ChristopherSmith - re: unnecessary quoting implique que vous savez à l'avance que $PATH est non nulle. "$PATH" fait en sorte que PATH soit nul ou non. De même si $1 contient des caractères susceptibles de perturber l'analyseur de commandes. Mettre la regex entre guillemets "(^|:)$1(:|$)" empêche cela. - Jesse Chisholm
@JesseChisholm: En fait, je pense que le point de Christopher est que les règles sont différentes entre [[ et ]]. Je préfère citer tout ce qui peut avoir besoin d’être cité, sauf si cela provoque son échec, mais je crois qu’il a raison, et que les citations ne sont pas vraiment nécessaire autour $PATH. Par contre, il me semble que vous avez raison à propos de $1. - Scott


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
}

Lorsque vous avez besoin de $ HOME / bin pour apparaître exactement une fois au début de votre $ PATH et nulle part ailleurs, n'acceptez aucun substitut.


6
2017-08-17 13:31



Merci, c'est une belle solution élégante, mais j'ai trouvé que je devais faire PATH=${PATH/"... plutôt que PATH=${PATH//"... pour le faire fonctionner. - Mark Booth
La forme à double barre oblique doit correspondre à n'importe quel nombre de correspondances; la barre oblique unique ne correspond qu'au premier (recherchez "Substitution de modèle" dans la page de manuel bash). Pas sûr pourquoi ça n'a pas marché ... - andybuckley
Cela échoue dans le cas inhabituel que $1 est la seule entrée (pas de deux-points). L'entrée est doublée. - Dennis Williamson
Il supprime également trop agressivement comme indiqué par PeterS6g. - Dennis Williamson


Voici une solution alternative qui présente l'avantage supplémentaire de supprimer les entités redondantes:

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

L'argument unique de cette fonction est ajouté au PATH et la première instance de la même chaîne est supprimée du chemin existant. En d'autres termes, si le répertoire existe déjà dans le chemin, il est promu au début plutôt que d'être ajouté en double.

La fonction fonctionne en ajoutant un deux-points au chemin pour vous assurer que toutes les entrées ont un signe deux points au début, puis en ajoutant la nouvelle entrée au chemin existant avec cette entrée supprimée. La dernière partie est réalisée à l'aide de bash ${var//pattern/sub} notation; voir le manuel de bash pour plus de détails.


6
2018-01-21 12:29



Bonne pensée mise en œuvre défectueuse. Considérez ce qui se passe si vous avez déjà /home/robert dans ton PATH et toi pathadd /home/rob. - Scott


Voici le mien (je crois que cela a été écrit il y a des années par Oscar, le sysadmin de mon ancien laboratoire, tout le mérite), ça fait des années que je suis bashrc. Il a l'avantage supplémentaire de vous permettre d'ajouter ou d'ajouter le nouveau répertoire à votre convenance:

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

Usage:

$ 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
2017-08-17 18:57