web-dev-qa-db-ja.com

emacs lispで複数のパスコンポーネントを単一の完全なパスに結合する正しい方法は何ですか?

ディレクトリとファイル名をそれぞれ表す文字列を含む変数dirfileがあるとします。 emacs LISPでそれらをファイルへのフルパスに結合する適切な方法は何ですか?

たとえば、dir"/usr/bin"で、file"ls"の場合、"/usr/bin/ls"が必要です。しかし、代わりにdir"/usr/bin/"である場合でも、スラッシュを繰り返さずに同じものが必要です。

51

Directory Names のマニュアルを読むと、答えが見つかります。

ディレクトリ名を指定すると、concatを使用して相対ファイル名と組み合わせることができます。

 (concat dirname relfile)

その前に、ファイル名が相対的であることを確認してください。絶対ファイル名を使用すると、結果が構文的に無効になるか、間違ったファイルを参照する可能性があります。

このような組み合わせでディレクトリファイル名を使用する場合は、最初にfile-name-as-directoryを使用してディレクトリファイル名に変換する必要があります。

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

のように、スラッシュを手で連結しようとしないでください

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

これは持ち運びできないからです。常にfile-name-as-directoryを使用してください。

便利なその他のコマンドは、file-name-directoryfile-name-nondirectory、および ファイル名コンポーネント セクションのその他のコマンドです。

59
Trey Jackson

これにはexpand-file-nameを使用できます。

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

編集:これは絶対ディレクトリ名でのみ機能します。 Treyの答えが望ましい解決策だと思います。

28
Hugh

複数のネストされたディレクトリをパスに結合したかったのです。もともと私は次のように複数のexpand-file-name呼び出しを使用しました。

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

ただし、これはかなり冗長であり、逆方向に読み取ります。

代わりに、Pythonのos.path.joinのように機能する関数を作成しました。

(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))))

それは次のように機能します:

(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"
10
dbr

これが私が使うものです:

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

@dbrとの違い:

  1. 「emacsディレクトリ名」、つまり末尾にスラッシュが付いた値を返します
  2. rootが相対的である場合、パスは展開されません(注を参照)
  3. rootをルートとして扱いますが、joindirs"/"で始まるfirstコンポーネントをルートとして使用しますルート。

メモ

多くのファイル処理関数(すべて、ほとんど、???)は、冗長なスラッシュを正規化し、相対パスでexpand-file-name(または同様のもの)を呼び出すため、#2と#3は実際には重要ではない場合があります。

3
sshaw

この質問は2010年に出されましたが、これを書いている時点では、 "join file path in elisp"のような検索でトップヒットなので、私は答えを更新します。

2010年以来、Emacsの世界では物事が大きく変化しています。回答で簡単に言及されているため、これは多少重複した回答です 以下 ですが、少し具体的に説明します。ファイルの相互作用のための 専用ライブラリ があります f.el

@ magnars の優れた s.el および dash.elf.el は現代的ですEmacsでファイルとディレクトリを操作するためのAPI。

車輪の再発明を試みないでください。このライブラリは、ファイルパスの操作に使用する必要があります。必要な関数は f-join

(f-join "path")                   ;; => "path"
(f-join "path" "to")              ;; => "path/to"
(f-join "/" "path" "to" "heaven") ;; => "/path/to/heaven"

最初にパッケージをインストールする必要がある場合があります。 MELPAで利用できるはずです。

2
JackCC

便利なファイルおよびディレクトリ操作ライブラリを使用する場合 f.el 必要なのは f-join だけです。以下のコードは、何らかの理由でこのライブラリの使用を拒否する人のためのものです。

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

これはPythonの os.path.join とまったく同じように動作します。

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

string-suffix-pは存在しません Emacs 24.4より前 なので、私は Emacs LISPで文字列がサフィックスで終わっているかどうかを確認してください で自分で書きました。

2