Quelqu'un a-t-il écrit une fonction bash pour ajouter un répertoire à $ PATH uniquement s'il ne s'y trouve pas déjà?
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, sauf si la session dans laquelle je suis est une session de connexion - ce qui n'est pas toujours vrai. Si je construis mon PATH dans .bashrc, il s'exécute avec chaque sous-shell. Donc, si je lance une fenêtre de terminal et que je lance ensuite screen, puis un script shell, je reçois:
$ echo $PATH
/usr/local/mysql/bin:/usr/local/mysql/bin:/usr/local/mysql/bin:....
Je vais essayer de créer une fonction bash appelée add_to_path()
qui n’ajoute le répertoire que 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.
Réponses:
De mon .bashrc:
Notez que PATH doit déjà être marqué comme exporté. Une réexportation n'est donc pas nécessaire. Ceci vérifie si le répertoire existe et est un répertoire avant de l'ajouter, ce qui ne vous intéressera peut-être pas.
De plus, cela ajoute le nouveau répertoire à la fin du chemin; pour mettre au début, utilisez à la
PATH="$1${PATH:+":$PATH"}"
place de laPATH=
ligne ci-dessus .la source
":$PATH:"
lieu de juste"$PATH"
${PATH:+"$PATH:"}
1 $"${variable:+value}
signifie vérifier sivariable
est défini et a une valeur non vide, et si c'est le cas, donne le résultat de l'évaluationvalue
. Fondamentalement, si PATH est non vide pour commencer, il le définit sur"$PATH:$1"
; si elle est vide, le réglage est juste"$1"
(notez l'absence de deux points).En développant la réponse de Gordon Davisson, cela soutient plusieurs arguments
Donc vous pouvez faire pathappend path1 path2 path3 ...
Pour prépending,
Semblable à pathappend, vous pouvez faire
pathprepend path1 path2 path3 ...
la source
pathprepend P1 P2 P3
et se retrouver avecPATH=P1:P2:P3
. Pour obtenir ce comportement, changerfor ARG in "$@" do
àfor ((i=$#; i>0; i--)); do ARG=${!i}
Voici quelque chose de ma réponse à cette question combinée à la structure de la fonction de Doug Harris. Il utilise des expressions régulières Bash:
la source
$1
au lieu de${1}
Mettez ceci dans les commentaires à la réponse sélectionnée, mais les commentaires ne semblent pas supporter la mise en forme PRE, ajoutez donc la réponse ici:
@ gordon-davisson Je ne suis pas un grand fan de citations et de concaténations inutiles. En supposant que vous utilisez une version de bash> = 3, vous pouvez utiliser les expressions rationnelles intégrées de bash et faire:
Cela gère correctement les cas où il y a des espaces dans le répertoire ou le PATH. On peut se demander si le moteur de regex intégré de bash est suffisamment lent pour être vraiment moins efficace que la concaténation et l’interpolation des chaînes utilisée par votre version, mais elle me semble plus esthétique encore.
la source
formatting using the backtick
ne prennent en charge que les résultats mais vous n’obtenez aucun contrôle de paragraphe décent.unnecessary quoting
implique que vous savez à l'avance que ce$PATH
n'est pas nul."$PATH"
rend OK si PATH est null ou non. De même si$1
contient des caractères qui pourraient confondre l'analyseur de commandes. Mettre l'expression rationnelle entre guillemets"(^|:)$1(:|$)"
empêche cela.[[
et]]
. Je préfère citer tout ce qui peut avoir besoin d'être cité, à moins qu'il cause à l' échec, mais je crois qu'il a raison, et que les cours ne sont pas vraiment nécessaires autour$PATH
. Par contre, il me semble que vous avez raison$1
.Lorsque vous avez besoin que $ HOME / bin apparaisse exactement une fois au début de votre $ PATH et nulle part ailleurs, n’acceptez aucun substitut.
la source
PATH=${PATH/"
... plutôt quePATH=${PATH//"
... pour que ça fonctionne.$1
est la seule entrée (pas de deux points). L'entrée devient doublée.Voici une solution alternative qui présente l’avantage supplémentaire de supprimer les ententes redondantes:
Le seul argument de cette fonction est ajouté au préfixe 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 lieu d'être ajouté en tant que duplicata.
La fonction ajoute deux points au chemin afin de s’assurer que toutes les entrées ont un signe deux-points au début, puis la nouvelle entrée est ajoutée au chemin existant avec cette entrée supprimée. La dernière partie est réalisée en utilisant la
${var//pattern/sub}
notation de bash ; voir le manuel bash pour plus de détails.la source
/home/robert
en vousPATH
et vouspathadd /home/rob
.Voici le mien (je crois que cela a été écrit il y a des années par Oscar, l'administrateur système de mon ancien laboratoire, lui revient entièrement), cela fait longtemps qu'il est dans ma poche. Il présente l’avantage supplémentaire de vous permettre d’ajouter ou d’ajouter le nouveau répertoire comme vous le souhaitez:
Usage:
la source
Pour le préfixe, j'aime bien la solution de @ Russell, mais il existe un petit bogue: si vous essayez de préfixer quelque chose comme "/ bin" sur un chemin "/ sbin: / usr / bin: / var / usr / bin: / usr / local / bin: / usr / sbin "il remplace" / bin: "3 fois (quand cela ne correspond pas du tout). En combinant un correctif pour cela avec la solution ajoutée de @ gordon-davisson, je comprends ceci:
la source
Un simple alias comme celui ci-dessous devrait faire l'affaire:
Tout ce qu'il fait est de diviser le chemin sur le caractère: et de comparer chaque composant avec l'argument que vous transmettez. Grep recherche une correspondance de ligne complète et affiche le nombre.
Exemple d'utilisation:
Remplacez la commande echo par addToPath ou un alias / fonction similaire.
la source
Voir Comment éviter de dupliquer la variable de chemin dans csh? sur StackOverflow pour un ensemble de réponses à cette question.
la source
Voici ce que j'ai fouetté:
Maintenant, dans .bashrc j'ai:
Version mise à jour suite au commentaire sur la façon dont mon original ne gérera pas les répertoires avec des espaces (merci à cette question de m'avoir indiqué d'utiliser
IFS
):la source
PATH
contient des espaces blancs,*
,?
ou[
...]
.Documents and Settings
etProgram Files
), mais Unix a déjà pris en charge les noms de chemins contenant des espaces, bien avant que Windoze n'existe.Je suis un peu surpris que personne n'en ait encore parlé, mais vous pouvez utiliser
readlink -f
pour convertir des chemins relatifs en chemins absolus et les ajouter au PATH en tant que tels.Par exemple, pour améliorer la réponse de Guillaume Perrault-Archambault,
devient
1. L'essentiel - à quoi sert-il?
La
readlink -f
commande convertira (entre autres choses) un chemin relatif en un chemin absolu. Cela vous permet de faire quelque chose comme2. Pourquoi testons-nous deux fois sur PATH?
Eh bien, considérons l'exemple ci-dessus. Si l'utilisateur dit à partir du répertoire une deuxième fois, sera . Bien sûr, ne sera pas présent dans . Mais alors sera mis à (l'équivalent absolu de chemin de ), qui est déjà dans . Nous devons donc éviter d’ajouter à une deuxième fois.
pathappend .
/path/to/my/bin/dir
ARG
.
.
PATH
ARGA
/path/to/my/bin/dir
.
PATH
/path/to/my/bin/dir
PATH
Peut-être plus important encore, l'objectif principal de
readlink
, comme son nom l'indique, est d'examiner un lien symbolique et de lire le chemin qu'il contient (c.-à-d. Qu'il pointe vers). Par exemple:Maintenant, si vous dites
pathappend /usr/lib/perl/5.14
, et vous avez déjà/usr/lib/perl/5.14
dans votre CHEMIN, eh bien, c'est bien; nous pouvons simplement le laisser tel quel. Mais, si ce/usr/lib/perl/5.14
n'est pas déjà dans votre PATH, nous appelonsreadlink
et obtenonsARGA
=/usr/lib/perl/5.14.2
, puis nous ajoutons cela àPATH
. Mais attendez une minute - si vous avez déjà ditpathappend /usr/lib/perl/5.14
, alors vous avez déjà/usr/lib/perl/5.14.2
dans votre PATH, et, encore une fois, nous devons vérifier cela pour éviter de l'ajouterPATH
une seconde fois.3. Quel est le problème
if ARGA=$(readlink -f "$ARG")
?En cas de doute, cette ligne teste si l'opération
readlink
réussit. Ceci est juste une bonne pratique de programmation défensive. Si nous allons utiliser le résultat de la commande m dans le cadre de la commande n (où m < n ), il est prudent de vérifier si la commande m a échoué et de le gérer d'une manière ou d'une autre. Je ne pense pas que cela risque d’readlink
échouer - mais, comme indiqué dans Comment récupérer le chemin absolu d’un fichier arbitraire sous OS X et ailleurs, ilreadlink
s’agit d’une invention de GNU. Comme il n'est pas spécifié dans POSIX, sa disponibilité dans Mac OS, Solaris et d'autres Unix non Linux est discutable. (En fait, je viens de lire un commentaire qui dit “readlink -f
ne semble pas fonctionner sous Mac OS X 10.11.6, maisrealpath
fonctionne " clé en main". Ainsi, si vous utilisez un système qui ne fonctionne pasreadlink
oureadlink -f
qui ne fonctionne pas, vous pourrez peut-être le modifier. script à utiliserrealpath
.) En installant un filet de sécurité, nous rendons notre code un peu plus portable.Bien sûr, si vous êtes sur un système qui n'a pas
readlink
(ourealpath
), vous ne voudrez pas le faire .pathappend .
Le deuxième
-d
test ([ -d "$ARGA" ]
) est probablement probablement inutile. Je ne peux pas penser à un scénario où$ARG
est un répertoire etreadlink
réussit, mais$ARGA
n'est pas un répertoire. Je viens de copier-coller la premièreif
déclaration pour créer la troisième et j'ai laissé le-d
test là-bas par paresse.4. Autres commentaires?
Ouais. Comme beaucoup d'autres réponses ici, celle-ci vérifie si chaque argument est un répertoire, le traite si c'est le cas et l'ignore s'il ne l'est pas. Cela peut être (ou non) adéquat si vous utilisez
pathappend
uniquement des.
fichiers “ ” (comme.bash_profile
et.bashrc
) et d’autres scripts. Mais, comme le montre cette réponse (ci-dessus), il est parfaitement possible de l’utiliser de manière interactive. Vous serez très perplexe si vous le faitesComme je l'ai dit plus haut, il est recommandé de gérer les erreurs. Voici un exemple:
la source
readlink -f "$ARG"
. (2) Je ne sais pas pourquoi cela se produirait (en particulier après que le-d "$ARG"
test a réussi), mais vous voudrez peut-être vérifier s'il areadlink
échoué. (3) Vous semblez négliger sareadlink
fonction première - mapper un nom de lien symbolique sur un "nom de fichier réel". Si (par exemple)/bin
est un lien symbolique vers/bin64
, des appels répétés àpathappend /bin
pourraient aboutir à un message PATH…:/bin64:/bin64:/bin64:/bin64:…
. Vous devriez probablement (aussi) vérifier si la nouvelle valeur de$ARG
est déjà dansPATH
..
dans mon exemple, je pense, peut être considéré comme un nom de fichier canonique. Correct?readlink
le nom de celui-ci en indique clairement le but - il regarde un lien symbolique et lit le chemin qu'il contient (c.-à-d. Qu'il pointe vers). (2) Eh bien, j'ai dit que je ne savais pas pourquoireadlink
cela échouerait. Je faisais remarquer que si un script ou une fonction contient plusieurs commandes, et que la commande n échouera de manière catastrophique (ou n’aura aucun sens) si la commande m échoue (où m < n ), il est prudent de vérifiez si la commande m a échoué et gérez cela d'une manière ou d'une autre - à tout le moins,… (suite)readlink
pourrait échouer si (a) le répertoire a été renommé ou supprimé (par un autre processus) entre vos appels àtest
etreadlink
, ou (b) s'il a/usr/bin/readlink
été supprimé (ou corrompu). (3) Vous semblez manquer mon point. Je ne vous encourage pas à reproduire d'autres réponses. Je dis qu'en vérifiant si l'originalARG
(à partir de la ligne de commande) est déjà entréPATH
, mais en ne répétant pas la vérification pour le nouveauARG
(le résultat dereadlink
), votre réponse est incomplète et donc incorrecte. … (Suite)la source
Cela fonctionne bien:
la source
Mes versions sont moins attentives aux chemins vides et insistent pour que les chemins soient valides et les répertoires que certaines postées ici, mais je trouve une collection volumineuse de prepend / append / clean / unique-ify / etc. les fonctions du shell doivent être utiles pour la manipulation du chemin. L'ensemble, dans leur état actuel, se trouve ici: http://pastebin.com/xS9sgQsX (commentaires et améliorations très bienvenus!)
la source
Vous pouvez utiliser un liner perl one:
La voici en bash:
la source
J'ai légèrement modifié la réponse de Gordon Davisson pour utiliser le répertoire actuel si aucun n'est fourni. Donc, vous pouvez simplement faire à
padd
partir du répertoire que vous voulez ajouter à votre PATH.la source
Vous pouvez vérifier si une variable personnalisée a été définie, sinon définissez-la puis ajoutez les nouvelles entrées:
Bien sûr, ces entrées peuvent toujours être dupliquées si elles sont ajoutées par un autre script, tel que
/etc/profile
.la source
Ce script vous permet d'ajouter à la fin de
$PATH
:Ou ajouter au début de
$PATH
:la source
Voici un moyen conforme à POSIX.
Il est cribbed de Guillaume Perrault-Archambault 's réponse à cette question et mike511 ' s réponse ici .
MISE À JOUR 2017-11-23: Correction d'un bug par @Scott
la source
/etc/profile
. Le répertoire que vous essayez d'ajouter à PATH est peut-être déjà présent plusieurs fois , et votre code y étouffe. … (Suite)/a/ck
à/b:/a/ck:/tr:/a/ck
, vous obtenez/a/ck:/b:/a/ck:/tr:/tr:/a/ck
.