Étant donné un chemin absolu ou relatif (dans un système de type Unix), je voudrais déterminer le chemin complet de la cible après avoir résolu tout lien symbolique intermédiaire. Points bonus pour résoudre également la notation ~ nom d'utilisateur en même temps.
Si la cible est un répertoire, il pourrait être possible de chdir () dans le répertoire puis d'appeler getcwd (), mais je veux vraiment le faire à partir d'un script shell plutôt que d'écrire un assistant C. Malheureusement, les shells ont tendance à cacher à l'utilisateur l'existence de liens symboliques (c'est bash sur OS X):
$ ls -ld foo bar
drwxr-xr-x 2 greg greg 68 Aug 11 22:36 bar
lrwxr-xr-x 1 greg greg 3 Aug 11 22:36 foo -> bar
$ cd foo
$ pwd
/Users/greg/tmp/foo
$
Ce que je veux, c'est une fonction resolver () telle que lorsqu'elle est exécutée à partir du répertoire tmp dans l'exemple ci-dessus, resolve ("foo") == "/ Users / greg / tmp / bar".
cd
d'abord le faire avant d'appelerpwd -P
. En d'autres termes: cela ne vous permettra pas de résoudre (voir la cible de) les liens symboliques vers des fichiers ou des liens symboliques cassés , et pour résoudre les liens symboliques des répertoires existants, vous devez effectuer un travail supplémentaire (restaurer le répertoire de travail précédent ou localiser les appelscd
etpwd -P
en sous-coque).Note de l'éditeur: ce qui précède fonctionne avec GNU
readlink
et FreeBSD / PC-BSD / OpenBSDreadlink
, mais pas sous OS X à partir de 10.11.GNU
readlink
offre des options supplémentaires et connexes, telles que la-m
résolution d'un lien symbolique, que la cible ultime existe ou non.Notez que depuis GNU coreutils 8.15 (2012-01-06), il existe un programme realpath qui est moins obtus et plus flexible que ce qui précède. Il est également compatible avec l'utilisation FreeBSD du même nom. Il inclut également des fonctionnalités pour générer un chemin relatif entre deux fichiers.
[ Ajout admin ci-dessous d'après le commentaire de halloleo - danorton]
Pour Mac OS X (via au moins 10.11.x), utilisez
readlink
sans l'-f
option:Note de l'éditeur: cela ne résoudra pas les liens symboliques de manière récursive et ne signalera donc pas la cible ultime ; Par exemple, étant donné un lien symbolique
a
qui pointe versb
, qui à son tour pointe versc
, cela ne fera que rapporterb
(et ne garantira pas qu'il est sorti comme un chemin absolu ).Utilisez la
perl
commande suivante sur OS X pour combler le vide de lareadlink -f
fonctionnalité manquante :perl -MCwd -le 'print Cwd::abs_path(shift)' "$path"
la source
readlink
fonctionne sur OSX, mais a besoin d'une autre syntaxe:readlink $path
sans le-f
."pwd -P" semble fonctionner si vous voulez juste le répertoire, mais si pour une raison quelconque vous voulez le nom de l'exécutable réel, je ne pense pas que cela aide. Voici ma solution:
la source
Un de mes favoris est
realpath foo
la source
realpath
sur Centos 6 avec GNU coreutils 8.4.31. J'en ai rencontré plusieurs autres sur Unix et Linux qui ont un coreutils GNU emballé sansrealpath
. Il semble donc que cela dépende de plus que de la seule version.realpath
plusreadlink
car il propose des drapeaux d'options tels que--relative-to
semble être exactement ce que vous demandez - il accepte un chemin arbitraire, résout tous les liens symboliques et retourne le "vrai" chemin - et c'est "standard * nix" que tous les systèmes ont probablement déjà
la source
Autrement:
la source
Assembler certaines des solutions données, sachant que readlink est disponible sur la plupart des systèmes, mais a besoin d'arguments différents, cela fonctionne bien pour moi sur OSX et Debian. Je ne suis pas sûr des systèmes BSD. Peut-être que la condition doit être
[[ $OSTYPE != darwin* ]]
d'exclure-f
d'OSX uniquement.la source
Voici comment obtenir le chemin réel du fichier sous MacOS / Unix en utilisant un script Perl en ligne:
De même, pour obtenir le répertoire d'un fichier avec lien symbolique:
la source
Votre chemin est-il un répertoire ou peut-il s'agir d'un fichier? S'il s'agit d'un répertoire, c'est simple:
Cependant, s'il peut s'agir d'un fichier, cela ne fonctionnera pas:
car le lien symbolique peut se résoudre en un chemin relatif ou complet.
Sur les scripts, je dois trouver le vrai chemin, afin de pouvoir référencer la configuration ou d'autres scripts installés avec lui, j'utilise ceci:
Vous pouvez définir
SOURCE
n'importe quel chemin de fichier. Fondamentalement, tant que le chemin est un lien symbolique, il résout ce lien symbolique. L'astuce est dans la dernière ligne de la boucle. Si le lien symbolique résolu est absolu, il l'utilisera commeSOURCE
. Cependant, s'il est relatif, il ajoutera leDIR
pour lui, qui a été résolu en un emplacement réel par la simple astuce que j'ai décrite en premier.la source
la source
Remarque: Je pense que c'est une solution solide, portable et prête à l'emploi, qui est invariablement longue pour cette même raison.
Vous trouverez ci-dessous un script / fonction entièrement compatible POSIX qui est donc multiplateforme (fonctionne également sur macOS, qui
readlink
ne prend toujours pas en charge à-f
partir de 10.12 (Sierra)) - il utilise uniquement les fonctionnalités du langage shell POSIX et uniquement les appels d'utilitaires compatibles POSIX .Il s'agit d'une implémentation portable de GNU
readlink -e
(la version la plus stricte dereadlink -f
).Vous pouvez exécuter le scénario avec
sh
ou la source de la fonction dansbash
,ksh
etzsh
:Par exemple, à l'intérieur d'un script, vous pouvez l'utiliser comme suit pour obtenir le vrai répertoire d'origine du script en cours d'exécution, avec les liens symboliques résolus:
rreadlink
définition de script / fonction:Le code a été adapté avec gratitude à partir de cette réponse .
J'ai également créé une
bash
version d'utilitaire autonome basée ici , que vous pouvez installer avecnpm install rreadlink -g
, si vous avez Node.js installé.Une tangente sur la sécurité:
jarno , en référence à la fonction assurant que la fonction intégrée
command
n'est pas masquée par un alias ou une fonction shell du même nom, demande dans un commentaire:La motivation derrière l'
rreadlink
assurance que celacommand
a sa signification d'origine est de l'utiliser pour contourner les alias de commodité (bénins) et les fonctions souvent utilisées pour masquer les commandes standard dans des shells interactifs, telles que la redéfinitionls
pour inclure les options favorites.Je pense qu'il est sûr de dire que si vous avez affaire à un non fiable, environnement malveillant, se soucier
unalias
ouunset
- ou, pour cette matière,while
,do
, ... - redéfinition n'est pas une préoccupation.Il y a quelque chose sur lequel la fonction doit s'appuyer pour avoir sa signification et son comportement d'origine - il n'y a aucun moyen de contourner cela.
Le fait que les shells de type POSIX permettent de redéfinir les codes internes et même les mots clés de langage est intrinsèquement un risque pour la sécurité (et l'écriture de code paranoïaque est difficile en général).
Pour répondre spécifiquement à vos préoccupations:
La fonction repose sur
unalias
et aunset
sa signification d'origine. Les redéfinir en tant que fonctions shell d'une manière qui modifie leur comportement serait un problème; la redéfinition en tant qu'alias n'est pas nécessairement une préoccupation, car le fait de citer (en partie) le nom de la commande (par exemple\unalias
) contourne les alias.Cependant, citant est pas une option pour shell mots - clés (
while
,for
,if
,do
, ...) et alors que les mots - clés shell faire préséance sur shell fonctions , dansbash
etzsh
alias ont la plus haute priorité, afin de se prémunir contre redéfinitions shell-mot clé que vous devez exécuterunalias
avec leurs noms (bien que dans lesbash
shells non interactifs (tels que les scripts), les alias ne sont pas développés par défaut - uniquement s'ilsshopt -s expand_aliases
sont explicitement appelés en premier).Pour vous assurer que
unalias
- en tant que fonction intégrée - a sa signification d'origine, vous devez d'abord l'utiliser\unset
dessus, ce qui nécessite queunset
sa signification d'origine:unset
est un shell intégré , donc pour vous assurer qu'il est invoqué en tant que tel, vous devez vous assurer qu'il n'est pas redéfini en tant que fonction . Bien que vous puissiez contourner un formulaire d'alias avec des guillemets, vous ne pouvez pas contourner un formulaire de fonction shell - catch 22.Ainsi, à moins que vous ne puissiez compter sur
unset
pour avoir sa signification d'origine, d'après ce que je peux dire, il n'y a aucun moyen garanti de se défendre contre toutes les redéfinitions malveillantes.la source
[
comme un alias dansdash
etbash
et comme une fonction shell dansbash
.while
en fonctionbash
,ksh
etzsh
(mais pasdash
), mais seulement avec lafunction <name>
syntaxe:function while { echo foo; }
œuvres (while() { echo foo; }
ne fonctionne pas). Cependant, cela n'occultera pas lewhile
mot - clé , car les mots-clés ont une priorité plus élevée que les fonctions (la seule façon d'appeler cette fonction est as\while
). Dansbash
etzsh
, les alias ont une priorité plus élevée que les mots-clés, donc les redéfinitions d' alias des mots-clés les masquent (maisbash
par défaut uniquement dans les shells interactifs , sauf si ellesshopt -s expand_aliases
sont explicitement appelées).Les scripts shell communs doivent souvent trouver leur répertoire "home" même s'ils sont invoqués en tant que lien symbolique. Le script doit donc trouver sa position "réelle" à partir de 0 $ seulement.
sur mon système imprime un script contenant les éléments suivants, ce qui devrait être un bon indice de ce dont vous avez besoin.
la source
Essaye ça:
la source
Depuis que je l'ai rencontré plusieurs fois au fil des ans, et cette fois-ci, j'avais besoin d'une version portable bash pure que je pouvais utiliser sur OSX et linux, j'ai continué et j'en ai écrit une:
La version vivante vit ici:
https://github.com/keen99/shell-functions/tree/master/resolve_path
mais pour le bien de SO, voici la version actuelle (je pense qu'elle est bien testée .. mais je suis ouvert aux commentaires!)
Ce ne sera peut-être pas difficile de le faire fonctionner pour du shell bourne ordinaire (sh), mais je n'ai pas essayé ... J'aime trop $ FUNCNAME. :)
voici un exemple classique, grâce à brew:
utilisez cette fonction et elle renverra le chemin -real-:
la source
Pour contourner l'incompatibilité Mac, j'ai trouvé
Pas génial mais cross OS
la source
python -c "from os import path; print(path.realpath('${SYMLINK_PATH}'));"
aurait probablement plus de sens. Pourtant, au moment où vous devez utiliser Python à partir d'un script shell, vous devriez probablement utiliser Python et vous épargner le casse-tête des scripts shell multiplateforme.dirname
,basename
, etreadlink
sont externes utilitaires , non encastrés coque;dirname
etbasename
font partie de POSIX,readlink
ne l'est pas.php
vient avec OS X. Cependant, même si le corps de la question mentionne OS X, il n'est pas étiqueté comme tel, et c'est devenu clair que les gens sur diverses plates-formes viennent ici pour des réponses, il convient donc de souligner ce qui est spécifique à la plate-forme / non-POSIX.Il s'agit d'un résolveur de lien symbolique dans Bash qui fonctionne que le lien soit un répertoire ou un non-répertoire:
Notez que tout
cd
etset
tout se déroule dans un sous-shell.la source
{}
autour du()
n'est pas inutile s'il n'y en a pas()
derrière le nom de la fonction. Bash accepte les déclarations de fonctions sans()
car le shell n'a pas de listes de paramètres dans les déclarations et ne fait pas d'appels avec,()
donc les déclarations de fonctions avec()
n'ont pas beaucoup de sens.Ici, je présente ce que je pense être une solution multiplateforme (Linux et macOS au moins) à la réponse qui fonctionne bien pour moi actuellement.
Voici une solution macOS (uniquement?). Peut-être mieux adapté à la question d'origine.
la source
Ma réponse ici Bash: comment obtenir le vrai chemin d'un lien symbolique?
mais bref très pratique dans les scripts:
Ceux-ci font partie de coreutils GNU, adaptés à une utilisation dans les systèmes Linux.
Pour tout tester, nous mettons le lien symbolique dans / home / test2 /, modifions quelques éléments supplémentaires et l'exécutons / l'appelons depuis le répertoire racine:
Où
la source
Dans le cas où pwd ne peut pas être utilisé (par exemple, appeler des scripts à partir d'un emplacement différent), utilisez realpath (avec ou sans dirname):
Fonctionne à la fois lors de l'appel via (plusieurs) liens symboliques ou lors de l'appel direct du script - depuis n'importe quel emplacement.
la source