247 votes

Comment normaliser un chemin de fichier dans Bash ?

Je veux transformer /foo/bar/.. a /foo

Existe-t-il une commande bash qui permet de faire cela ?


Edit : dans mon cas pratique, le répertoire existe bien.

4 votes

Est-ce important si /foo/bar ou même /foo existent réellement, ou êtes-vous seulement intéressé par l'aspect de la manipulation des chaînes de caractères selon les règles des noms de chemin ?

5 votes

@twalberg ... c'est un peu artificiel...

2 votes

@CamiloMartin Pas du tout artificiel - il fait exactement ce que la question demande - il transforme. /foo/bar/.. a /foo et en utilisant un bash commande. S'il existe d'autres exigences qui ne sont pas mentionnées, alors peut-être devraient-elles l'être...

242voto

Kent Fredric Points 35592

Si vous voulez couper une partie d'un nom de fichier à partir du chemin, "dirname" et "basename" sont vos amis, et "realpath" est pratique aussi.

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 realpath n'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 sens que le chemin n'a pas besoin d'exister pour être normalisé.

6 votes

Pour ceux d'entre vous qui ont besoin d'une solution OS X, consultez la réponse d'Adam Liss ci-dessous.

0 votes

stackoverflow.com/a/17744637/999943 Il s'agit d'une réponse fortement liée ! Je suis tombé sur ces deux messages d'AQ en une seule journée, et je voulais les relier entre eux.

0 votes

Realpath semble avoir été ajouté à coreutils en 2012. Voir l'historique du fichier à github.com/coreutils/coreutils/commits/master/src/realpath.c .

110voto

Tim Whitcomb Points 5267

Je ne sais pas s'il existe une commande bash directe pour faire ça, mais je fais habituellement

normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"

et cela fonctionne bien.

12 votes

Cela normalisera mais ne résoudra pas les liens mous. Il peut s'agir d'un bogue ou d'une fonctionnalité :-)

5 votes

Il y a aussi 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, et pas seulement un "foo" qui est dans le répertoire courant. Je pense que vous devez faire quelque chose comme : CDPATH="" cd "${dirToNormalize}" && pwd -P.

0 votes

Je ne connaissais pas le CDPATH - ça a l'air très bien ! Je peux cependant imaginer des situations où la possibilité de référencer le répertoire dans CDPATH est exactement ce que vous voulez. L'utilisation d'un caractère de tête ./ le réparer aussi ?

64voto

Adam Liss Points 27815

Essayez realpath . Vous trouverez ci-dessous l'intégralité de la source, qui fait l'objet d'un don 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>

static char *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));
    return 0;
}    

void usage(void)
{
    fprintf(stderr, "usage: %s PATH\n", s_pMyName);
    exit(1);
}

1 votes

Est-ce que cela fait partie de l'installation standard de bash ? J'obtiens "command not found" sur notre système (bash 3.00.15(1) sur RHEL).

4 votes

gnu.org/software/coreutils pour readlink, mais realpath vient de ce paquet : paquets.debian.org/unstable/utils/realpath

0 votes

Jay, Kent, merci d'avoir vérifié Realpath. Je n'étais pas au courant qu'un utilitaire était disponible chez debian ; celui ci-dessus est bien plus simple et a été développé indépendamment. La version Debian fournit des fonctionnalités supplémentaires.

45voto

loevborg Points 839

Une solution portable et fiable consiste à utiliser python, qui est préinstallé à peu près partout (y compris sur Darwin). Vous avez deux options :

  1. 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

  2. realpath renvoie un chemin absolu et, ce faisant, résout 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, path/to/file peut être un chemin relatif ou absolu.

7 votes

Merci, c'est la seule qui a fonctionné. readlink ou realpath ne sont pas disponibles sous OS X. Python devrait l'être sur la plupart des plateformes.

1 votes

Juste pour clarifier, readlink est disponible sous OS X, mais pas avec la fonction -f option. Solutions de contournement portables discutées aquí .

5 votes

Je n'en reviens pas que ce soit la seule solution sensée si vous ne voulez pas suivre les liens. La méthode Unix, mon cul.

35voto

mattalxndr Points 2100

Utilisez l'utilitaire readlink du paquet coreutils.

MY_PATH=$(readlink -f "$0")

3 votes

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 l'utilisation de -f si vous voulez la portabilité, beaucoup d'OS-es sont affectés.

2 votes

@sorin. La question ne porte pas sur Mac, mais sur Linux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X