63

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

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

4

8 に答える 8

78

ディレクトリ名のマニュアルを読むと、答えが見つかります。

ディレクトリ名を指定すると、次を使用して相対ファイル名と組み合わせることができます 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-directoryの、、file-name-nondirectoryおよびその他です。

于 2010-10-19T01:50:46.110 に答える
32

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

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

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

于 2010-10-19T01:22:15.200 に答える
12

複数のネストされたディレクトリをパスに結合したかったのです。もともと私は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"
于 2012-11-20T13:07:32.240 に答える
8

この質問は2010年に出されたものですが、執筆時点では「elispでファイルパスを結合する」などの検索でトップヒットだったので、回答を更新したいと思いました。

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

@magnarsの優れたs.eldash.elに大きく影響を受けた、f.elは、Emacsでファイルとディレクトリを操作するための最新のAPIです。

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

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

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

于 2019-06-04T13:15:14.840 に答える
4

便利なファイルとディレクトリの操作ライブラリを使用する場合は、必要なのは。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-pEmacs 24.4より前には存在しなかったので、EmacsLispで文字列がサフィックスで終わっているかどうかを確認するで自分で書いた。

于 2014-03-14T11:43:41.277 に答える
4

これが私が使用するものです:

(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コンポーネントを使用します。"/"

ノート

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

于 2015-09-27T19:36:26.160 に答える
0

Emacsのマニュアルへのリンクで前に言ったことを完了するためだけに:

他の人が以前に言ったように、OPの質問への答えはを使用することexpand-file-nameです。これは組み込み関数であり、Cで実装されているため、外部ライブラリを使用する必要はありません。

これは、 「ファイル名を拡張する関数」というタイトルのEmacsLispマニュアルのセクションで説明されています。

そして、Emacsのオンラインヘルプによると、この機能はバージョン... 1.6のEmacsで導入されました!だから...それは利用できるはずです!

于 2021-05-22T15:45:05.570 に答える
0

2021年以降に質問に来た人のために。elisp組み込み関数file-name-concatがその仕事をします。今でははるかに簡単です。

ドキュメントは、次のキーストロークでemacsで見つけることができます。

C-h f file-name-concat <enter>

COMPONENTSをDIRECTORYに追加し、結果の文字列を返します。

COMPONENTSの要素は、文字列またはnilである必要があります。DIRECTORYまたはCOMPONENTSの非最終要素は、スラッシュで終了する場合と終了しない場合があります。スラッシュで終了しない場合は、汚染する前にスラッシュが挿入されます。

その他の関連する機能は、file-nameグループに記載されています。おそらくEmacsバージョン28.1
以前に導入されました。 この関数は、一致データを含むグローバル状態を変更しません。

(file-name-concat "/usr/bin/" "ls")
;; ==> "/usr/bin/ls"

(file-name-concat "/usr" "bin" "ls")
;; ==> "/usr/bin/ls"
于 2022-01-15T13:09:06.277 に答える