Est-ce important /foo/barou même d' /fooexister réellement, ou êtes-vous uniquement intéressé par l'aspect de manipulation de chaîne selon les règles de nom de chemin?
Chen Levy
5
@twalberg ... c'est un peu artificiel ...
Camilo Martin
2
@CamiloMartin Pas arrangea du tout - il fait exactement ce que la question demande - se transforme /foo/bar/..à /foo, et en utilisant une bashcommande. S'il y a d'autres exigences qui ne sont pas énoncées, alors peut-être qu'elles devraient être ...
twalberg
14
@twalberg Vous en avez fait trop TDD -_- '
Camilo Martin
Réponses:
193
si vous voulez couper une partie d'un nom de fichier à partir du chemin, "dirname" et "basename" sont vos amis, et "realpath" est également pratique.
dirname /foo/bar/baz
# /foo/bar
basename /foo/bar/baz
# baz
dirname $( dirname /foo/bar/baz )# /foo
realpath ../foo
# ../foo: No such file or directory
realpath /tmp/../tmp/../tmp
# /tmp
realpath alternatives
Si realpathn'est pas supporté par votre shell, vous pouvez essayer
readlink -f /path/here/..
Aussi
readlink -m /path/there/../../
Fonctionne de la même manière que
realpath -s /path/here/../../
en ce que le chemin n'a pas besoin d'exister pour être normalisé.
Pour ceux d'entre vous qui ont besoin d'une solution OS X, consultez la réponse d'Adam Liss ci-dessous.
Trenton
stackoverflow.com/a/17744637/999943 Ceci est une réponse fortement liée! Je suis tombé sur ces deux postes QA en une journée et je voulais les lier ensemble.
Les deux realpathet readlinkproviennent des utilitaires de base GNU, donc vous obtenez probablement les deux ou aucun. Si je me souviens bien, la version de readlink sur Mac est légèrement différente de celle de GNU: - \
Antoine 'hashar' Musso
100
Je ne sais pas s'il existe une commande bash directe pour le faire, mais je le fais habituellement
Cela normalisera mais ne résoudra pas les liens logiciels. Cela peut être un bug ou une fonctionnalité. :-)
Adam Liss
4
Il a également un problème si $ CDPATH est défini; parce que le "cd foo" basculera dans n'importe quel répertoire "foo" qui est un sous-répertoire de $ CDPATH, pas seulement un "foo" qui se trouve dans le répertoire courant. Je pense que vous devez faire quelque chose comme: CDPATH = "" cd "$ {dirToNormalize}" && pwd -P.
mjs
7
La réponse de Tim est certainement la plus simple et la plus portable. CDPATH est facile à gérer: dir = "$ (unset CDPATH && cd" $ dir "&& pwd)"
David Blevins
2
Cela pourrait être très dangereux ( rm -rf $normalDir) si dirToNormalize n'existe pas!
Frank Meulenaar
4
Ouais, il est probablement préférable d'utiliser un &&selon le commentaire de @ DavidBlevins.
Elias Dorneles
54
Essayez realpath. Vous trouverez ci-dessous la source dans son intégralité, par la présente donnée au domaine public.
// realpath.c: display the absolute path to a file or directory.// Adam Liss, August, 2007// This program is provided "as-is" to the public domain, without express or// implied warranty, for any non-profit use, provided this notice is maintained.#include<stdio.h>#include<stdlib.h>#include<string.h>#include<libgen.h>#include<limits.h>staticchar*s_pMyName;void usage(void);int main(int argc,char*argv[]){char
sPath[PATH_MAX];
s_pMyName = strdup(basename(argv[0]));if(argc <2)
usage();
printf("%s\n", realpath(argv[1], sPath));return0;}void usage(void){
fprintf(stderr,"usage: %s PATH\n", s_pMyName);
exit(1);}
Vous devriez vraiment parcourir la partie avec "Bonus: c'est une commande bash, et disponible partout!" partie sur realpath. Il se trouve que c'est aussi mon préféré, mais au lieu de compléter votre outil à partir de la source. Tout est trop lourd, et la plupart du temps vous voulez juste readlink -f... BTW, readlinkn'est PAS non plus un bash intégré, mais fait partie d' coreutilsUbuntu.
Tomasz Gandor
4
Vous avez dit que vous donniez cela au domaine public, mais l'en-tête indique également "pour toute utilisation à but non lucratif" - voulez-vous vraiment le faire don au domaine public? Cela signifierait qu'il peut être utilisé à des fins commerciales à but lucratif ainsi qu'à des fins non lucratives…
me_and
36
Une solution portable et fiable consiste à utiliser python, qui est préinstallé à peu près partout (y compris Darwin). Vous avez deux options:
abspath renvoie un chemin absolu mais ne résout pas les liens symboliques:
BSD n'a pas le drapeau -f, ce qui signifie que cela échouera même sur le dernier MacOS Mojave et de nombreux autres systèmes. Oubliez d'utiliser -f si vous voulez la portabilité, de nombreux systèmes d'exploitation sont affectés.
sorin
1
@sorin. La question ne concerne pas Mac, c'est Linux.
mattalxndr
14
readlinkest la norme bash pour obtenir le chemin absolu. Il a également l'avantage de renvoyer des chaînes vides si des chemins ou un chemin n'existe pas (étant donné les drapeaux pour le faire).
Pour obtenir le chemin absolu vers un répertoire qui peut exister ou non, mais dont les parents existent, utilisez:
abspath=$(readlink -f $path)
Pour obtenir le chemin absolu vers un répertoire qui doit exister avec tous les parents:
abspath=$(readlink -e $path)
Pour canoniser le chemin donné et suivre les liens symboliques s'ils existent, mais sinon ignorer les répertoires manquants et retourner le chemin quand même, c'est:
abspath=$(readlink -m $path)
Le seul inconvénient est que readlink suivra les liens. Si vous ne souhaitez pas suivre les liens, vous pouvez utiliser cette convention alternative:
abspath=$(cd ${path%/*}&& echo $PWD/${path##*/})
Cela va chdir vers la partie répertoire de $ path et afficher le répertoire courant avec la partie fichier de $ path. S'il échoue à chdir, vous obtenez une chaîne vide et une erreur sur stderr.
readlinkest une bonne option si elle est disponible. La version OS X ne prend pas en charge les options -eou -f. Dans les trois premiers exemples, vous devez avoir des guillemets $pathpour gérer les espaces ou les caractères génériques dans le nom de fichier. +1 pour l'extension des paramètres, mais cela présente une vulnérabilité de sécurité. Si pathest vide, ce sera cddans votre répertoire personnel. Vous avez besoin de guillemets doubles. abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
toxalot
Ce n'était qu'un exemple. Si vous êtes déterminé à la sécurité, vous ne devriez vraiment pas utiliser bash ou toute autre variante de shell. En outre, bash a ses propres problèmes en ce qui concerne la compatibilité entre plates-formes, ainsi que les problèmes de changement de fonctionnalité entre les principales versions. OSX n'est qu'une des nombreuses plates-formes avec des problèmes liés aux scripts shell, sans oublier qu'elle est basée sur BSD. Lorsque vous devez être vraiment multi-plateforme, vous devez être conforme à POSIX, donc l'expansion des paramètres sort vraiment de la fenêtre. Jetez un œil à Solaris ou à HP-UX.
Craig
6
Il ne s'agit pas ici d'une infraction, mais il est important de signaler des problèmes obscurs comme celui-ci. Je veux juste une réponse rapide à ce problème trivial et j'aurais fait confiance à ce code avec n'importe quelle entrée / s'il n'y avait pas eu le commentaire ci-dessus. Il est également important de prendre en charge OS-X dans ces discussions bash. Il existe de nombreuses commandes qui ne sont malheureusement pas prises en charge sur OS-X, et de nombreux forums tiennent cela pour acquis lorsque nous discutons de Bash, ce qui signifie que nous continuerons à rencontrer de nombreux problèmes multiplates-formes à moins qu'il ne soit traité plus tôt que tard.
Rebs
13
Ancienne question, mais il existe un moyen beaucoup plus simple si vous traitez des noms de chemin complets au niveau du shell:
abspath = "$ (cd" $ path "&& pwd)"
Comme le cd se produit dans un sous-shell, il n'a pas d'impact sur le script principal.
Deux variantes, en supposant que vos commandes intégrées au shell acceptent -L et -P, sont:
abspath = "$ (cd -P" $ path "&& pwd -P)" # chemin physique avec liens symboliques résolus
abspath = "$ (cd -L" $ path "&& pwd -L)" # chemin logique préservant les liens symboliques
Personnellement, j'ai rarement besoin de cette approche ultérieure, sauf si je suis fasciné par les liens symboliques pour une raison quelconque.
FYI: variation sur l'obtention du répertoire de départ d'un script qui fonctionne même si le script change son répertoire actuel plus tard.
name0 = "$ (nom de base" $ 0 ")"; #base nom du script
dir0 = "$ (cd" $ (dirname "$ 0") "&& pwd)"; #absolute dir de départ
L'utilisation de CD garantit que vous avez toujours le répertoire absolu, même si le script est exécuté par des commandes telles que ./script.sh qui, sans cd / pwd, donne souvent juste .. Inutile si le script fait un cd plus tard.
Comme l'a noté Adam Liss, il realpathn'est pas fourni avec chaque distribution. C'est dommage car c'est la meilleure solution. Le code source fourni est génial, et je vais probablement commencer à l'utiliser maintenant. Voici ce que j'ai utilisé jusqu'à présent, que je partage ici juste pour être complet:
get_abs_path(){local PARENT_DIR=$(dirname "$1")
cd "$PARENT_DIR"local ABS_PATH="$(pwd)"/"$(basename "$1")"
cd ->/dev/null
echo "$ABS_PATH"}
Si vous voulez qu'il résout les liens symboliques, remplacez simplement pwdpar pwd -P.
Un piège avec l' pwd -Poption pour ce cas ... Réfléchissez à ce qui se passerait s'il y $(basename "$1")avait un lien symbolique vers un fichier dans un autre répertoire. Le pwd -Pseul résout les liens symboliques dans la partie répertoire du chemin, mais pas la partie nom de base.
Je soupçonne que cela échoue si l'argument n'est pas un répertoire. Supposons que je veuille savoir où mène / usr / bin / java?
Edward Falk
1
Si vous savez que c'est un fichier, vous pouvez pushd $(dirname /usr/bin/java)essayer.
schmunk
5
Pas exactement une réponse mais peut-être une question de suivi (la question d'origine n'était pas explicite):
readlinkest bien si vous voulez réellement suivre les liens symboliques. Mais il existe également un cas d'utilisation pour simplement normaliser ./et ../et //séquences, ce qui peut être fait de manière purement syntaxique, sans canoniser les liens symboliques. readlinkn'est pas bon pour cela, et non plus realpath.
for f in $paths;do(cd $f; pwd);done
fonctionne pour les chemins existants, mais rompt pour les autres.
Un sedscript semble être un bon pari, sauf que vous ne pouvez pas remplacer de manière itérative des séquences ( /foo/bar/baz/../..-> /foo/bar/..-> /foo) sans utiliser quelque chose comme Perl, ce qui n'est pas sûr à assumer sur tous les systèmes, ou en utilisant une boucle laide pour comparer la sortie de sedto son entrée.
FWIW, un monoligne utilisant Java (JDK 6+):
jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths
realpatha une -spossibilité de ne pas résoudre les liens symboliques et que les références à résoudre /./, /../et de supprimer supplémentaires /caractères. Lorsqu'il est combiné avec l' -moption, realpathne fonctionne que sur le nom du fichier et ne touche aucun fichier réel. Cela ressemble à la solution parfaite. Mais hélas, realpathil manque toujours sur de nombreux systèmes.
toxalot
La suppression de ..composants ne peut pas être effectuée syntaxiquement lorsque des liens symboliques sont impliqués. /one/two/../threen'est pas le même que /one/threesi twoest un lien symbolique vers /foo/bar.
jrw32982 prend en charge Monica
@ jrw32982 oui, comme je l'ai dit dans ma réponse, c'est pour un cas d'utilisation où la canonisation de lien symbolique n'est pas souhaitée ou nécessaire.
Jesse Glick
@JesseGlick ce n'est pas seulement une question de savoir si vous voulez ou non canoniser les liens symboliques. Votre algorithme produit en fait la mauvaise réponse. Pour que votre réponse soit correcte, vous devez savoir a priori qu'il n'y a pas de liens symboliques impliqués (ou qu'ils n'ont qu'une certaine forme). Votre réponse indique que vous ne voulez pas les canoniser, pas qu'il n'y a pas de liens symboliques impliqués dans le chemin.
jrw32982 prend en charge Monica
Il existe des cas d'utilisation où la normalisation doit être effectuée sans supposer de structure de répertoire existante fixe. La normalisation de l'URI est similaire. Dans ces cas, c'est une limitation inhérente que le résultat ne sera généralement pas correct s'il se trouve des liens symboliques près d'un répertoire où le résultat est appliqué ultérieurement.
Jesse Glick
5
Je suis en retard à la fête, mais c'est la solution que j'ai élaborée après avoir lu un tas de fils comme celui-ci:
resolve_dir(){(builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"`2>/dev/null;if[ $?-eq 0];then pwd;fi)}
Cela résoudra le chemin absolu de $ 1, jouera bien avec ~, gardera les liens symboliques dans le chemin où ils se trouvent, et cela ne gâchera pas votre pile de répertoires. Il retourne le chemin complet ou rien s'il n'existe pas. Il s'attend à ce que $ 1 soit un répertoire et échouera probablement si ce n'est pas le cas, mais c'est une vérification facile à faire vous-même.
Réponse bavarde et un peu tardive. Je dois en écrire un car je suis bloqué sur un RHEL4 / 5 plus ancien. Je gère les liens absolus et relatifs et simplifie les entrées //, /./ et somedir /../.
Essayez notre nouveau produit de bibliothèque Bash realpath-lib que nous avons placé sur GitHub pour une utilisation gratuite et sans encombre. Il est parfaitement documenté et constitue un excellent outil d'apprentissage.
Il résout les chemins locaux, relatifs et absolus et n'a aucune dépendance sauf Bash 4+; il devrait donc fonctionner à peu près partout. C'est gratuit, propre, simple et instructif.
function get_realpath(){if[[-f "$1"]]then# file *must* existif cd "$(echo "${1%/*}")"&>/dev/null
then# file *may* not be local# exception is ./file.ext# try 'cd .; cd -;' *works!*local tmppwd="$PWD"
cd -&>/dev/null
else# file *must* be locallocal tmppwd="$PWD"fielse# file *cannot* existreturn1# failurefi# reassemble realpath
echo "$tmppwd"/"${1##*/}"return0# success}
Il contient également des fonctions pour get_dirname, get_filename, get_ stemname et validate_path. Essayez-le sur toutes les plateformes et aidez-le à l'améliorer.
Le problème realpathest qu'il n'est pas disponible sur BSD (ou OSX d'ailleurs). Voici une recette simple extraite d' un article assez ancien (2009) du Linux Journal , qui est assez portable:
function normpath(){# Remove all /./ sequences.local path=${1//\/.\//\/}# Remove dir/.. sequences.while[[ $path =~([^/][^/]*/\.\./)]];do
path=${path/${BASH_REMATCH[0]}/}done
echo $path
}
Notez que cette variante ne nécessite pas non plus que le chemin existe.
Cependant, cela ne résout pas les liens symboliques. Realpath traite les chemins commençant à la racine et suit les liens symboliques au fur et à mesure de leur progression. Tout cela ne fait que réduire les références parentales.
Martijn Pieters
2
Sur la base de la réponse de @ Andre, je pourrais avoir une version légèrement meilleure, au cas où quelqu'un chercherait une solution sans boucle, entièrement basée sur la manipulation de chaînes. Il est également utile pour ceux qui ne veulent pas déréférencer les liens symboliques, ce qui est l'inconvénient d'utiliser realpathou readlink -f.
Il fonctionne sur les versions bash 3.2.25 et supérieures.
shopt -s extglob
normalise_path(){local path="$1"# get rid of /../ example: /one/../two to /two
path="${path//\/*([!\/])\/\.\./}"# get rid of /./ and //* example: /one/.///two to /one/two
path="${path//@(\/\.\/|\/+(\/))//}"# remove the last '/.'
echo "${path%%/.}"}
$ normalise_path /home/codemedic/../codemedic////.config
/home/codemedic/.config
C'est une bonne idée, mais j'ai perdu 20 minutes à essayer de le faire fonctionner sur différentes versions de bash. Il s'avère que l'option shell extglob doit être activée pour que cela fonctionne, et ce n'est pas le cas par défaut. En ce qui concerne la fonctionnalité bash, il est important de spécifier à la fois la version requise et les options non par défaut car ces détails peuvent varier selon les systèmes d'exploitation. Par exemple, une version récente de Mac OSX (Yosemite) n'est livrée qu'avec une version obsolète de bash (3.2).
drwatsoncode
Désolé @ricovox; Je les ai maintenant mis à jour. J'ai hâte de connaître la version exacte de Bash que vous avez là-bas. La formule ci-dessus (mise à jour) fonctionne sur CentOS 5.8 qui est fourni avec bash 3.2.25
ϹοδεMεδιϲ
Désolé pour la confusion. Ce code a fonctionné sur ma version de Mac OSX bash (3.2.57) une fois que j'avais activé extglob. Ma note sur les versions bash était plus générale (qui s'applique en fait plus à une autre réponse ici concernant l'expression régulière dans bash).
drwatsoncode
2
J'apprécie votre réponse cependant. Je l'ai utilisé comme base pour moi. Par ailleurs, j'ai remarqué plusieurs cas où le vôtre échoue: (1) Chemins relatifs hello/../world(2) Points dans le nom de fichier /hello/..world(3) Points après une double barre oblique /hello//../world(4) Point avant ou après une double barre oblique /hello//./worldou /hello/.//world (5) Parent après le courant: /hello/./../world/ (6) Parent après Parent:, /hello/../../worldetc. - Certains d'entre eux peuvent être corrigés en utilisant une boucle pour effectuer des corrections jusqu'à ce que le chemin cesse de changer. (Retirez également dir/../, /dir/..mais supprimez dir/..de la fin.)
drwatsoncode
0
Basé sur l'excellent extrait de python de loveborg, j'ai écrit ceci:
#!/bin/sh# Version of readlink that follows links to the end; good for Mac OS Xfor file in"$@";dowhile[-h "$file"];do
l=`readlink $file`case"$l"in/*) file="$l";;*) file=`dirname "$file"`/"$l"esacdone#echo $file
python -c "import os,sys; print os.path.abspath(sys.argv[1])""$file"done
Voici un cas de test court avec quelques espaces tordus dans les chemins pour exercer pleinement la citation
mkdir -p "/tmp/test/ first path "
mkdir -p "/tmp/test/ second path "
echo "hello">"/tmp/test/ first path / red .txt "
ln -s "/tmp/test/ first path / red .txt ""/tmp/test/ second path / green .txt "
cd "/tmp/test/ second path "
fullpath " green .txt "
cat " green .txt "
Je sais que c'est une question ancienne. J'offre toujours une alternative. Récemment, j'ai rencontré le même problème et je n'ai trouvé aucune commande existante et portable pour le faire. J'ai donc écrit le script shell suivant qui comprend une fonction qui peut faire l'affaire.
#! /bin/sh function normalize {local rc=0local ret
if[ $# -gt 0 ] ; then# invalidif["x`echo $1 | grep -E '^/\.\.'`"!="x"];then
echo $1
return-1fi# convert to absolute pathif["x`echo $1 | grep -E '^\/'`"=="x"];then
normalize "`pwd`/$1"return $?fi
ret=`echo $1 | sed 's;/\.\($\|/\);/;g' | sed 's;/[^/]*[^/.]\+[^/]*/\.\.\($\|/\);/;g'`else
read line
normalize "$line"return $?fiif["x`echo $ret | grep -E '/\.\.?(/|$)'`"!="x"];then
ret=`normalize "$ret"`
rc=$?fi
echo "$ret"return $rc
}
J'ai créé une fonction intégrée pour gérer cela en mettant l'accent sur les performances les plus élevées possibles (pour le plaisir). Il ne résout pas les liens symboliques, c'est donc essentiellement le même que realpath -sm.
## A bash-only mimic of `realpath -sm`. ## Give it path[s] as argument[s] and it will convert them to clean absolute paths
abspath (){
${*+false}&&{>&2 echo $FUNCNAME: missing operand;return1;};local c s p IFS='/';## path chunk, absolute path, input path, IFS for splitting paths into chunkslocal-i r=0;## return valuefor p in"$@";docase"$p"in## Check for leading backslashes, identify relative/absolute path'')((r|=1));continue;;//[!/]*)>&2 echo "paths =~ ^//[^/]* are impl-defined; not my problem";((r|=2));continue;;/*);;*) p="$PWD/$p";;## Prepend the current directory to form an absolute pathesac
s='';for c in $p;do## Let IFS split the path at '/'scase $c in### NOTE: IFS is '/'; so no quotes needed here''|.);;## Skip duplicate '/'s and '/./'s..) s="${s%/*}";;## Trim the previous addition to the absolute path string*) s+=/$c;;### NOTE: No quotes here intentionally. They make no difference, it seemsesac;done;
echo "${s:-/}";## If xpg_echo is set, use `echo -E` or `printf $'%s\n'` insteaddonereturn $r;}
Remarque: Cette fonction ne gère pas les chemins commençant par //, car exactement deux doubles barres obliques au début d'un chemin correspondent à un comportement défini par l'implémentation. Cependant, il gère /,/// et ainsi de suite.
Cette fonction semble gérer correctement tous les cas marginaux, mais il y en a peut-être encore que je n'ai pas traités.
Remarque sur les performances: lorsqu'il est appelé avec des milliers d'arguments, il abspathest environ 10 fois plus lent que realpath -sm; lorsqu'il est appelé avec un seul argument, abspaths'exécute> 110 fois plus vite que realpath -smsur ma machine, principalement parce qu'il n'est pas nécessaire d'exécuter un nouveau programme à chaque fois.
la stat -f %N ~/Documentsligne est un hareng rouge ... votre shell remplace ~/Documentspar /Users/me/Documents, et stataffiche simplement son argument textuellement.
/foo/bar
ou même d'/foo
exister réellement, ou êtes-vous uniquement intéressé par l'aspect de manipulation de chaîne selon les règles de nom de chemin?/foo/bar/..
à/foo
, et en utilisant unebash
commande. S'il y a d'autres exigences qui ne sont pas énoncées, alors peut-être qu'elles devraient être ...Réponses:
si vous voulez couper une partie d'un nom de fichier à partir du chemin, "dirname" et "basename" sont vos amis, et "realpath" est également pratique.
realpath
alternativesSi
realpath
n'est pas supporté par votre shell, vous pouvez essayerAussi
Fonctionne de la même manière que
en ce que le chemin n'a pas besoin d'exister pour être normalisé.
la source
realpath
etreadlink
proviennent des utilitaires de base GNU, donc vous obtenez probablement les deux ou aucun. Si je me souviens bien, la version de readlink sur Mac est légèrement différente de celle de GNU: - \Je ne sais pas s'il existe une commande bash directe pour le faire, mais je le fais habituellement
et ça marche bien.
la source
rm -rf $normalDir
) si dirToNormalize n'existe pas!&&
selon le commentaire de @ DavidBlevins.Essayez
realpath
. Vous trouverez ci-dessous la source dans son intégralité, par la présente donnée au domaine public.la source
realpath
. Il se trouve que c'est aussi mon préféré, mais au lieu de compléter votre outil à partir de la source. Tout est trop lourd, et la plupart du temps vous voulez justereadlink -f
... BTW,readlink
n'est PAS non plus un bash intégré, mais fait partie d'coreutils
Ubuntu.Une solution portable et fiable consiste à utiliser python, qui est préinstallé à peu près partout (y compris Darwin). Vous avez deux options:
abspath
renvoie un chemin absolu mais ne résout pas les liens symboliques:python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
realpath
renvoie un chemin absolu et résout ainsi les liens symboliques, générant un chemin canonique:python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
Dans chaque cas, il
path/to/file
peut s'agir d'un chemin relatif ou absolu.la source
readlink
est disponible sur OS X, mais pas avec l'-f
option. Solutions de contournement portables discutées ici .Utilisez l'utilitaire readlink du package coreutils.
la source
readlink
est la norme bash pour obtenir le chemin absolu. Il a également l'avantage de renvoyer des chaînes vides si des chemins ou un chemin n'existe pas (étant donné les drapeaux pour le faire).Pour obtenir le chemin absolu vers un répertoire qui peut exister ou non, mais dont les parents existent, utilisez:
Pour obtenir le chemin absolu vers un répertoire qui doit exister avec tous les parents:
Pour canoniser le chemin donné et suivre les liens symboliques s'ils existent, mais sinon ignorer les répertoires manquants et retourner le chemin quand même, c'est:
Le seul inconvénient est que readlink suivra les liens. Si vous ne souhaitez pas suivre les liens, vous pouvez utiliser cette convention alternative:
Cela va chdir vers la partie répertoire de $ path et afficher le répertoire courant avec la partie fichier de $ path. S'il échoue à chdir, vous obtenez une chaîne vide et une erreur sur stderr.
la source
readlink
est une bonne option si elle est disponible. La version OS X ne prend pas en charge les options-e
ou-f
. Dans les trois premiers exemples, vous devez avoir des guillemets$path
pour gérer les espaces ou les caractères génériques dans le nom de fichier. +1 pour l'extension des paramètres, mais cela présente une vulnérabilité de sécurité. Sipath
est vide, ce seracd
dans votre répertoire personnel. Vous avez besoin de guillemets doubles.abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
Ancienne question, mais il existe un moyen beaucoup plus simple si vous traitez des noms de chemin complets au niveau du shell:
Comme le cd se produit dans un sous-shell, il n'a pas d'impact sur le script principal.
Deux variantes, en supposant que vos commandes intégrées au shell acceptent -L et -P, sont:
Personnellement, j'ai rarement besoin de cette approche ultérieure, sauf si je suis fasciné par les liens symboliques pour une raison quelconque.
FYI: variation sur l'obtention du répertoire de départ d'un script qui fonctionne même si le script change son répertoire actuel plus tard.
L'utilisation de CD garantit que vous avez toujours le répertoire absolu, même si le script est exécuté par des commandes telles que ./script.sh qui, sans cd / pwd, donne souvent juste .. Inutile si le script fait un cd plus tard.
la source
Comme l'a noté Adam Liss, il
realpath
n'est pas fourni avec chaque distribution. C'est dommage car c'est la meilleure solution. Le code source fourni est génial, et je vais probablement commencer à l'utiliser maintenant. Voici ce que j'ai utilisé jusqu'à présent, que je partage ici juste pour être complet:Si vous voulez qu'il résout les liens symboliques, remplacez simplement
pwd
parpwd -P
.la source
pwd -P
option pour ce cas ... Réfléchissez à ce qui se passerait s'il y$(basename "$1")
avait un lien symbolique vers un fichier dans un autre répertoire. Lepwd -P
seul résout les liens symboliques dans la partie répertoire du chemin, mais pas la partie nom de base.Ma solution récente était:
Basé sur la réponse de Tim Whitcomb.
la source
pushd $(dirname /usr/bin/java)
essayer.Pas exactement une réponse mais peut-être une question de suivi (la question d'origine n'était pas explicite):
readlink
est bien si vous voulez réellement suivre les liens symboliques. Mais il existe également un cas d'utilisation pour simplement normaliser./
et../
et//
séquences, ce qui peut être fait de manière purement syntaxique, sans canoniser les liens symboliques.readlink
n'est pas bon pour cela, et non plusrealpath
.fonctionne pour les chemins existants, mais rompt pour les autres.
Un
sed
script semble être un bon pari, sauf que vous ne pouvez pas remplacer de manière itérative des séquences (/foo/bar/baz/../..
->/foo/bar/..
->/foo
) sans utiliser quelque chose comme Perl, ce qui n'est pas sûr à assumer sur tous les systèmes, ou en utilisant une boucle laide pour comparer la sortie desed
to son entrée.FWIW, un monoligne utilisant Java (JDK 6+):
la source
realpath
a une-s
possibilité de ne pas résoudre les liens symboliques et que les références à résoudre/./
,/../
et de supprimer supplémentaires/
caractères. Lorsqu'il est combiné avec l'-m
option,realpath
ne fonctionne que sur le nom du fichier et ne touche aucun fichier réel. Cela ressemble à la solution parfaite. Mais hélas,realpath
il manque toujours sur de nombreux systèmes...
composants ne peut pas être effectuée syntaxiquement lorsque des liens symboliques sont impliqués./one/two/../three
n'est pas le même que/one/three
sitwo
est un lien symbolique vers/foo/bar
.Je suis en retard à la fête, mais c'est la solution que j'ai élaborée après avoir lu un tas de fils comme celui-ci:
Cela résoudra le chemin absolu de $ 1, jouera bien avec ~, gardera les liens symboliques dans le chemin où ils se trouvent, et cela ne gâchera pas votre pile de répertoires. Il retourne le chemin complet ou rien s'il n'existe pas. Il s'attend à ce que $ 1 soit un répertoire et échouera probablement si ce n'est pas le cas, mais c'est une vérification facile à faire vous-même.
la source
Réponse bavarde et un peu tardive. Je dois en écrire un car je suis bloqué sur un RHEL4 / 5 plus ancien. Je gère les liens absolus et relatifs et simplifie les entrées //, /./ et somedir /../.
la source
Essayez notre nouveau produit de bibliothèque Bash realpath-lib que nous avons placé sur GitHub pour une utilisation gratuite et sans encombre. Il est parfaitement documenté et constitue un excellent outil d'apprentissage.
Il résout les chemins locaux, relatifs et absolus et n'a aucune dépendance sauf Bash 4+; il devrait donc fonctionner à peu près partout. C'est gratuit, propre, simple et instructif.
Tu peux faire:
Cette fonction est au cœur de la bibliothèque:
Il contient également des fonctions pour get_dirname, get_filename, get_ stemname et validate_path. Essayez-le sur toutes les plateformes et aidez-le à l'améliorer.
la source
Le problème
realpath
est qu'il n'est pas disponible sur BSD (ou OSX d'ailleurs). Voici une recette simple extraite d' un article assez ancien (2009) du Linux Journal , qui est assez portable:Notez que cette variante ne nécessite pas non plus que le chemin existe.
la source
Sur la base de la réponse de @ Andre, je pourrais avoir une version légèrement meilleure, au cas où quelqu'un chercherait une solution sans boucle, entièrement basée sur la manipulation de chaînes. Il est également utile pour ceux qui ne veulent pas déréférencer les liens symboliques, ce qui est l'inconvénient d'utiliser
realpath
oureadlink -f
.Il fonctionne sur les versions bash 3.2.25 et supérieures.
la source
hello/../world
(2) Points dans le nom de fichier/hello/..world
(3) Points après une double barre oblique/hello//../world
(4) Point avant ou après une double barre oblique/hello//./world
ou/hello/.//world
(5) Parent après le courant:/hello/./../world/
(6) Parent après Parent:,/hello/../../world
etc. - Certains d'entre eux peuvent être corrigés en utilisant une boucle pour effectuer des corrections jusqu'à ce que le chemin cesse de changer. (Retirez égalementdir/../
,/dir/..
mais supprimezdir/..
de la fin.)Basé sur l'excellent extrait de python de loveborg, j'ai écrit ceci:
la source
Cela fonctionne même si le fichier n'existe pas. Il nécessite que le répertoire contenant le fichier existe.
la source
realpath -e
J'avais besoin d'une solution qui ferait les trois:
realpath
etreadlink -f
sont des addonsAucune des réponses n'avait à la fois # 1 et # 2. J'ai ajouté le numéro 3 pour sauver les autres rasages de yak.
Voici un cas de test court avec quelques espaces tordus dans les chemins pour exercer pleinement la citation
la source
Je sais que c'est une question ancienne. J'offre toujours une alternative. Récemment, j'ai rencontré le même problème et je n'ai trouvé aucune commande existante et portable pour le faire. J'ai donc écrit le script shell suivant qui comprend une fonction qui peut faire l'affaire.
https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c
la source
J'ai créé une fonction intégrée pour gérer cela en mettant l'accent sur les performances les plus élevées possibles (pour le plaisir). Il ne résout pas les liens symboliques, c'est donc essentiellement le même que
realpath -sm
.Remarque: Cette fonction ne gère pas les chemins commençant par
//
, car exactement deux doubles barres obliques au début d'un chemin correspondent à un comportement défini par l'implémentation. Cependant, il gère/
,///
et ainsi de suite.Cette fonction semble gérer correctement tous les cas marginaux, mais il y en a peut-être encore que je n'ai pas traités.
Remarque sur les performances: lorsqu'il est appelé avec des milliers d'arguments, il
abspath
est environ 10 fois plus lent querealpath -sm
; lorsqu'il est appelé avec un seul argument,abspath
s'exécute> 110 fois plus vite querealpath -sm
sur ma machine, principalement parce qu'il n'est pas nécessaire d'exécuter un nouveau programme à chaque fois.la source
J'ai découvert aujourd'hui que vous pouvez utiliser la
stat
commande pour résoudre les chemins.Donc pour un répertoire comme "~ / Documents":
Vous pouvez exécuter ceci:
stat -f %N ~/Documents
Pour obtenir le chemin complet:
/Users/me/Documents
Pour les liens symboliques, vous pouvez utiliser l'option de format% Y:
stat -f %Y example_symlink
Ce qui pourrait retourner un résultat comme:
/usr/local/sbin/example_symlink
Les options de formatage peuvent être différentes sur d'autres versions de * NIX, mais elles fonctionnent pour moi sur OSX.
la source
stat -f %N ~/Documents
ligne est un hareng rouge ... votre shell remplace~/Documents
par/Users/me/Documents
, etstat
affiche simplement son argument textuellement.Une solution simple utilisant
node.js
:la source