Un moyen plus confortable de modifier un long $ PATH?

35

Je veux ajouter, dans ~ / .bashrc, quelques répertoires à mon $ PATH.

Mon $ PATH est assez long, il est donc un peu difficile de voir quels répertoires il contient et dans quel ordre.

Je sais que je peux modifier mon ~ / .bashrc pour être:

PATH=$PATH:/some/dir
PATH=$PATH:/another/dir:/yet/another
PATH=$PATH:/and/another
...

cela rendrait la lecture plus facile. Mais je me demandais si au cours des dernières années, Bash avait acquis une syntaxe facilitant la spécification d’un long PATH. Par exemple, je fantasme sur une syntaxe semblable à:

PATH=:((
  /some/dir
  /another/dir
  /yet/another
  /and/another
  ...
))

Je sais qu'une telle syntaxe n'est pas valide. Je me demandais s'il y avait quelque chose d'aussi facile. Y a-t-il?

Niccolo M.
la source
Le tutoriel traditionnel pour définir le chemin via PATH=foo:$PATHsemble erronée , car elle à maintenir la croissance à chaque fois source ~/.bashrcet même exec bashne peuvent pas aider puisque le $PATHest export.

Réponses:

25

J'utilise un ensemble de fonctions pratiques pour ajouter ou ajouter un chemin à une variable. Les fonctions sont incluses dans l'archive de distribution pour Bash dans un fichier contrib appelé "pathfuncs".

  • add_path ajoutera l'entrée à la fin de la variable PATH
  • pre_path ajoutera l'entrée au début de la variable PATH
  • del_path supprimera l'entrée de la variable PATH, où qu'elle se trouve

Si vous spécifiez une variable comme second argument, il l'utilisera à la place de PATH.

Pour plus de commodité, les voici:

# is $1 missing from $2 (or PATH) ?
no_path() {
    eval "case :\$${2-PATH}: in *:$1:*) return 1;; *) return 0;; esac"
}
# if $1 exists and is not in path, append it
add_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="\$${2:-PATH}:$1"
}
# if $1 exists and is not in path, prepend it
pre_path () {
  [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="$1:\$${2:-PATH}"
}
# if $1 is in path, remove it
del_path () {
  no_path $* || eval ${2:-PATH}=`eval echo :'$'${2:-PATH}: |
    sed -e "s;:$1:;:;g" -e "s;^:;;" -e "s;:\$;;"`
}

Si vous les ajoutez à votre fichier de démarrage bash, vous pouvez ajouter à votre PATH comme ceci:

pre_path $HOME/bin
add_path /sbin
add_path /usr/sbin

Ou spécifiez une variable différente:

pre_path $HOME/man MANPATH
pre_path $HOME/share/man MANPATH
add_path /usr/local/man MANPATH
add_path /usr/share/man MANPATH

J'utilise cette méthode dans mes fichiers rc en plaçant les pre_paths en premier et les add_paths en second. Cela rend tous les changements de mon chemin faciles à comprendre en un coup d'œil. Un autre avantage est que les lignes sont suffisamment courtes pour que je puisse ajouter un commentaire de fin sur une ligne si nécessaire.

Et comme il s’agit de fonctions, vous pouvez les utiliser de manière interactive à partir de la ligne de commande, par exemple en ajoutant add_path $(pwd)d’ajouter le répertoire en cours au chemin.

Étoile de mer
la source
Merci. J'ai vérifié votre code et ça marche. Étonnamment, j'ai également trouvé un usage pour del_path (un "." S'insinue dans mon chemin dans certaines situations, diable sait d'où, alors je l'ai fait del_path .).
Niccolo M.
Salut. il est possible de source (inclure) ce pathfuncs à partir du script bashrc ou dois-je les copier / coller ici?
Cristiano
@ Cristiano Soit travaillera. C'est vraiment comme tu veux.
Starfish
11

OK, j’ai trouvé la solution suivante, qui me semble élégante (en ce qui concerne la syntaxe du shell). Il utilise la syntaxe de tableau de Bash et aussi un moyen astucieux de joindre des éléments:

paths=(
  /some/dir
  /another/dir
  '/another/dir with spaces in it'
  /yet/another
  /and/another
  /end
)
paths_joined=$( IFS=: ; echo "${paths[*]}" )

PATH=$paths_joined:$PATH

ALERTE!

Il se trouve que cette solution a un problème : contrairement aux solutions de @terdon et @Starfish, elle ne vérifie pas d’abord si les chemins sont déjà dans PATH. Donc, puisque je veux mettre ce code dans ~ / .bashrc (et non dans ~ / .profile), les chemins en double seront insérés dans PATH. Donc n'utilisez pas cette solution (à moins que vous ne la mettiez dans ~ / .profile (ou, mieux, ~ / .bash_profile car elle a la syntaxe spécifique à Bash)).

Niccolo M.
la source
1
Très agréable. Pouvez-vous s'il vous plaît accepter une réponse afin que les autres ne viennent pas ici pour proposer une solution alors que vous en avez déjà trouvée une? Merci
base
Les chemins dupliqués ne sont pas vraiment un problème. Il est très peu probable que vous ayez ajouté suffisamment de répertoires pour PATHcauser des problèmes de performances (en particulier parce que le shell met en cache les recherches réussies).
Chepner
5

J'utilise la fonction ci-dessous dans mon ~/.bashrc. C'est quelque chose que j'ai reçu d'un administrateur système de mon ancien laboratoire mais je ne pense pas qu'il l'ait écrit. Ajoutez simplement ces lignes à votre ~/.profileou ~/.bashrc:

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

Cela présente divers avantages:

  • L' ajout de nouveaux répertoires à l' $PATHest trivial: pathmunge /foo/bar;
  • Cela évite les entrées en double;
  • Vous pouvez choisir d’ajouter une nouvelle entrée au début ( pathmunge /foo/barou à la fin ( pathmunge /foo/baraprès) du $PATH.

Le fichier d'initialisation de votre shell contiendrait alors quelque chose comme:

pathmunge /some/dir
pathmunge /another/dir
pathmunge '/another/dir with spaces in it'
pathmunge /yet/another
pathmunge /and/another
pathmunge /end
terdon
la source
Merci beaucoup Mais je choisirai la solution de @ Starfish car sa solution ne se reproduit pas grep.
Niccolo M.
2
@NiccoloM. pas de problème, acceptez ce que vous préférez. Soyez prudent avec l'approche de starfish, elle exécutera du code arbitraire via le evalfichier afin que vous puissiez causer des dommages sérieux si vous l'exécutez avec le mauvais argument.
Terdon
Notez que la fonction plus rapide existe dans RedHat de le faire sans commande externe grep, voir bugzilla.redhat.com/show_bug.cgi?id=544652#c7
林果皞
4

Je veux ajouter, dans ~ / .bashrc, quelques répertoires à mon $ PATH.

J'utilise ce qui suit dans Cygwin. Cela devrait fonctionner dans d'autres versions de bash. Vous pouvez supprimer le unset PATHpour construire sur votre courant PATH(si vous faites cela, vous devrez peut-être savoir comment ajouter les :séparateurs correctement).

Remarque:

  • Une fois, j'ai eu cette fonctionnalité dans une bashfonction, mais je l'ai perdue après un crash de disque.

Dans mon .bash_profile:

# Build up the path using the directories in ~/.path_elements
unset PATH
while read line; do 
  PATH="${PATH}$line"; 
done < ~/.path_elements

...

# Add current directory to path
export PATH=".:${PATH}"

Dans ~/.path_elements:

/home/DavidPostill/bin:
/usr/local/bin:
/usr/bin:
/c/Windows/system32:
/c/Windows
DavidPostill
la source
Merci. Votre réponse m'a inspiré à travailler sur une solution similaire. (À mon goût, stocker les chemins dans un fichier séparé est une gêne.)
Niccolo M.
1

J'utilise ceci dans mon .bashrc (et aussi mon .zshrc, puisque j'utilise typiquement zsh lorsqu'il est disponible au lieu de bash). Certes, il me faut ajouter manuellement des répertoires, mais un avantage est que, lors de la mise à jour, je peux continuer à le copier sur de nouveaux serveurs sans me soucier du PATH sur un nouveau serveur créé avec des répertoires qui n'existent pas.

##
## CHEMIN
##
## Au lieu de simplement éponger notre chemin avec des répertoires qui peuvent
## ne convient pas pour ce serveur, essayez d’être intelligent à propos de ce que nous ajoutons
##
PATH = / usr / local / sbin: / usr / local / bin: / usr / sbin: / usr / bin: / sbin: / bin
[-d / cs / sbin] && PATH = / cs / sbin: $ PATH
[-d / cs / bin] && PATH = / cs / bin: $ PATH
[-d / usr / ucb] && PATH = $ PATH: / usr / ucb
[-d / usr / ccs / bin] && CHEMIN = $ CHEMIN: / usr / ccs / bin
[-d / usr / local / ssl / bin] && PATH = $ CHEMIN: / usr / local / ssl / bin
[-d / usr / krb5 / bin] && PATH = $ PATH: / usr / krb5 / bin
[-d / usr / krb5 / sbin] && PATH = $ PATH: / usr / krb5 / sbin
[-d / usr / kerberos / sbin] && CHEMIN = $ CHEMIN: / usr / kerberos / sbin
[-d / usr / kerberos / bin] && PATH = $ PATH: / usr / kerberos / bin
[-d /cs/local/jdk1.5.0/bin] && PATH = $ PATH: /cs/local/jdk1.5.0/bin
[-d /usr/java/jre1.5.0_02/bin] && PATH = $ PATH: /usr/java/jre1.5.0_02/man
[-d /usr/java1.2/bin] && PATH = $ PATH: /usr/java1.2/bin
[-d /cs/local/perl5.8.0/bin] && PATH = $ PATH: /cs/local/perl5.8.0/bin
[-d / usr / perl5 / bin] && PATH = $ CHEMIN: / usr / perl5 / bin
[-d / usr / X11R6 / bin] && CHEMIN = $ CHEMIN: / usr / X11R6 / bin
[-d / etc / X11] && PATH = $ PATH: / etc / X11
[-d / opt / sfw / bin] && PATH = $ CHEMIN: / opt / sfw / bin
[-d / usr / local / apache / bin] && PATH = $ PATH: / usr / local / apache / bin
[-d / usr / apache / bin] && PATH = $ CHEMIN: / usr / apache / bin
[-d / cs / admin / bin] && PATH = $ CHEMIN: / cs / admin / bin
[-d / usr / openwin / bin] && PATH = $ PATH: / usr / openwin / bin
[-d / usr / xpg4 / bin] && PATH = $ PATH: / usr / xpg4 / bin
[-d / usr / dt / bin] && CHEMIN = $ CHEMIN: / usr / dt / bin

Je fais la même chose pour mon MANPATH:

##
# # MANPATH
##
## Au lieu de simplement assombrir notre MANPATH avec des répertoires pouvant
## ne convient pas pour ce serveur, essayez d’être intelligent à propos de ce que nous ajoutons
##
MANPATH = / usr / local / man
[-d / usr / share / man] && MANPATH = $ MANPATH: / usr / share / man
[-d / usr / local / share / man] && MANPATH = $ MANPATH: / usr / local / share / man
[-d / usr / man] && MANPATH = $ MANPATH: / usr / man
[-d / cs / man] && MANPATH = $ MANPATH: / cs / man
[-d / usr / krb5 / man] && MANPATH = $ MANPATH: / usr / krb5 / man
[-d / usr / kerberos / man] && MANPATH = $ MANPATH: / usr / kerberos / man
[-d / usr / local / ssl / man] && MANPATH = $ MANPATH: / usr / local / ssl / man
[-d /cs/local/jdk1.5.0/man] && MANPATH = $ MANPATH: /cs/local/jdk1.5.0/man
[-d /usr/java/jre1.5.0_02/man] && MANPATH = $ MANPATH: /usr/java/jre1.5.0_02/man
[-d /usr/java1.2/man] && MANPATH = $ MANPATH: /usr/java1.2/man
[-d / usr / X11R6 / man] && MANPATH = $ MANPATH: / usr / X11R6 / man
[-d / usr / local / apache / man] && MANPATH = $ MANPATH: / usr / local / apache / man
[-d / usr / local / mysql / man] && MANPATH = $ MANPATH: / usr / local / mysql / man
[-d /cs/local/perl5.8.0/man] && MANPATH = $ MANPATH: /cs/local/perl5.8.0/man
[-d / usr / perl5 / man] && MANPATH = $ MANPATH: / usr / perl5 / man
[-d / usr / local / perl / man] && MANPATH = $ MANPATH: / usr / local / perl / man
[-d /usr/local/perl5.8.0/man] && MANPATH = $ MANPATH: /usr/local/perl5.8.0/man
[-d / usr / openwin / man] && MANPATH = $ MANPATH: / usr / openwin / man

En plus d'avoir un seul fichier que je peux copier sur des systèmes dans des environnements disparates sans craindre d'ajouter des répertoires inexistants à PATH, cette approche a également l'avantage de me permettre de spécifier l'ordre dans lequel je veux que les répertoires apparaissent dans PATH. Comme la première ligne de chaque définition redéfinit complètement la variable PATH, je peux mettre à jour mon fichier .bashrc et le rechercher après la modification pour mettre à jour mon shell sans ajouter d'entrées en double (ce que j'avais l'habitude de connaître il y a longtemps " $ PATH = $ PATH: / new / dir ". Cela me permet d’obtenir une copie vierge dans l’ordre que je désire.

Brian Snook
la source
1
suggérant une alternative: d="/usr/share/man" ; [ -d "$d" ] && MANPATH="$MANPATH:${d}"il sera plus court et plus facile d'ajouter un nouveau répertoire (il suffit de copier une ligne et d'éditer la première partie "d = ...."). Cependant, dans le cas de PATH, je pense que vous allez vous retrouver avec beaucoup trop de répertoires dans votre PATH, ce qui n’est pas toujours bon (que se passe-t-il si une commande "foo" existe dans l’un des chemins les moins connus et fait quelque chose de complètement différent qu'attend un utilisateur régulier?)
Olivier Dulac
Le PO a demandé un moyen plus concis d’ajouter des chemins. C'est beaucoup plus verbeux et plus sujet aux erreurs que ce qu'ils essayaient déjà d'éviter.
underscore_d
-1

Il y a un moyen facile! Lire les fonctions du shell et les variables de chemin d'accès dans Linux Journal , 1 mars 2000 Par Stephen Collyer

Les fonctions me permettent d’utiliser un nouveau type de données dans mon environnement bash - la liste séparée par des deux-points. En plus de PATH, je les utilise pour ajuster mon LOCATE_PATH, MANPATH et d’autres, ainsi qu’en tant que type de données général dans la programmation bash. Voici comment configurer mon PATH (en utilisant les fonctions):

# Add my bin directory to $PATH at the beginning, so it overrides 
addpath -f -p PATH $HOME/bin

# For Raspberry Pi development (add at end)
addpath -b -p PATH ${HOME}/rpi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin

# remove nonexistent directories from PATH
delpath -n -p PATH

# ensure PATH contains unique entries
uniqpath -p PATH

Étant donné que le lien Linux Journal est appelé "rompu", j'ai placé les fonctions de chemin d'accès Bash dans un fichier .shar à l'adresse http://pastebin.ubuntu.com/13299528/

Waltinator
la source
3
Quelles sont ces fonctions? Comment le PO les utilise-t-il? Elles sont vraisemblablement décrites dans le lien brisé dans votre réponse, c’est précisément pourquoi nous voulons toujours que les réponses soient autonomes. Veuillez éditer et inclure les fonctions réelles dans votre réponse.
Terdon
@terdon: link fonctionne pour moi, j'ai mis un fichier .shar sur pastebin, je ne posterai pas de lignes de 1K ici.
Waltinator
Oups, travaille pour moi aussi maintenant. Ce n'est pas quand j'ai laissé le commentaire. Quoi qu'il en soit, l'essentiel de mon commentaire était que nous essayions d'éviter de faire des liens avec des ressources externes dans les réponses. Nous voulons que les informations soient où elles peuvent être mises à jour, modifiées et sont immunisées contre les liens.
Terdon