Quelle est la manière correcte de joindre plusieurs composants de chemin d’access dans un seul chemin complet dans emacs lisp?

Supposons que je dispose de variables dir et file contenant des chaînes représentant respectivement un répertoire et un nom de fichier. Quelle est la manière appropriée dans emacs lisp de les joindre dans un chemin complet vers le fichier?

Par exemple, si dir est "/usr/bin" et si le file est "ls" , alors je veux "/usr/bin/ls" . Mais si à la place dir est "/usr/bin/" , je veux toujours la même chose, sans répétition de barre oblique.

En parcourant le manuel pour les noms de répertoire , vous trouverez la réponse:

Étant donné un nom de répertoire, vous pouvez le combiner avec un nom de fichier relatif en utilisant concat :

  (concat dirname relfile) 

Assurez-vous de vérifier que le nom du fichier est relatif avant de le faire. Si vous utilisez un nom de fichier absolu, les résultats peuvent être syntaxiquement incorrects ou faire référence au mauvais fichier.

Si vous souhaitez utiliser un nom de fichier de répertoire pour créer une telle combinaison, vous devez d’abord le convertir en nom de répertoire en utilisant file-name-as-directory :

  (concat (file-name-as-directory dirfile) relfile) 

N’essayez pas de concaténer une barre oblique à la main, comme dans

  ;;; Wrong! (concat dirfile "/" relfile) 

parce que ce n’est pas portable. Toujours utiliser file-name-as-directory .

Les autres commandes utiles sont: file-name-directory file-name-nondirectory , file-name-nondirectory et autres dans la section Composants de nom de fichier .

Vous pouvez utiliser expand-file-name pour ceci:

 (expand-file-name "ls" "/usr/bin") "/usr/bin/ls" (expand-file-name "ls" "/usr/bin/") "/usr/bin/ls" 

Edit: cela ne fonctionne qu’avec des noms de répertoires absolus. Je pense que la réponse de Trey est la solution préférable.

Je voulais joindre plusieurs répertoires nesteds sur un chemin. A l’origine, j’ai utilisé plusieurs appels expand-file-name , comme ceci:

 (expand-file-name "b" (expand-file-name "a" "/tmp")) "/tmp/a/b" 

Cependant, c’est plutôt verbeux et lit à l’envers.

Au lieu de cela, j’ai écrit une fonction qui agit comme os.path.join de Python:

 (defun joindirs (root &rest dirs) "Joins a series of directories together, like Python's os.path.join, (dotemacs-joindirs \"/tmp\" \"a\" \"b\" \"c\") => /tmp/a/b/c" (if (not dirs) root (apply 'joindirs (expand-file-name (car dirs) root) (cdr dirs)))) 

Cela fonctionne comme ça:

 (joindirs "/tmp" "a" "b") "/tmp/a/b" (joindirs "~" ".emacs.d" "src") "/Users/dbr/.emacs.d/src" (joindirs "~" ".emacs.d" "~tmp") "/Users/dbr/.emacs.d/~tmp" 

Si vous utilisez une bibliothèque pratique de manipulation de fichiers et de répertoires, vous n’avez besoin que de f-join . Le code ci-dessous est pour ceux qui, pour une raison quelconque, refusent d’utiliser cette bibliothèque.

 (defun os-path-join (a &rest ps) (let ((path a)) (while ps (let ((p (pop ps))) (cond ((ssortingng-prefix-p "/" p) (setq path p)) ((or (not path) (ssortingng-suffix-p "/" p)) (setq path (concat path p))) (t (setq path (concat path "/" p)))))) path)) 

Cela se comporte exactement comme os.path.join de Python.

 ELISP> (os-path-join "~" "a" "b" "") "~/a/b/" ELISP> (os-path-join "~" "a" "/b" "c") "/b/c" 

ssortingng-suffix-p n’existe pas avant Emacs 24.4 , j’ai donc écrit le mien à Vérifier si une chaîne se termine par un suffixe dans Emacs Lisp .

Voici ce que j’utilise:

 (defun catdir (root &rest dirs) (apply 'concat (mapcar (lambda (name) (file-name-as-directory name)) (push root dirs)))) 

Différences de @ dbr’s:

  1. Renvoie un “nom de répertoire emacs”, c’est-à-dire une valeur avec une barre oblique finale
  2. Il ne développe pas le chemin si root est relatif (voir notes)
  3. Traite root comme racine, alors que joindirs utilisera le premier composant commençant par "/" comme racine.

Remarques

De nombreuses fonctions de traitement de fichiers (toutes, la plupart, ???) normaliseront les barres obliques redondantes et appeleront expand-file-name (ou similaire) sur des chemins relatifs, donc # 2 et # 3 n’ont pas vraiment d’importance.