ディレクトリとファイル名をそれぞれ表す変数dir
と文字列が含まれているとします。file
emacs lispでそれらをファイルへのフルパスに結合する適切な方法は何ですか?
たとえば、dir
is"/usr/bin"
とfile
is"ls"
の場合、が必要です"/usr/bin/ls"
。しかし、代わりにdir
である場合"/usr/bin/"
でも、スラッシュを繰り返さずに同じものが必要です。
ディレクトリとファイル名をそれぞれ表す変数dir
と文字列が含まれているとします。file
emacs lispでそれらをファイルへのフルパスに結合する適切な方法は何ですか?
たとえば、dir
is"/usr/bin"
とfile
is"ls"
の場合、が必要です"/usr/bin/ls"
。しかし、代わりにdir
である場合"/usr/bin/"
でも、スラッシュを繰り返さずに同じものが必要です。
ディレクトリ名のマニュアルを読むと、答えが見つかります。
ディレクトリ名を指定すると、次を使用して相対ファイル名と組み合わせることができます
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
およびその他です。
これに使用できますexpand-file-name
:
(expand-file-name "ls" "/usr/bin")
"/usr/bin/ls"
(expand-file-name "ls" "/usr/bin/")
"/usr/bin/ls"
編集:これは絶対ディレクトリ名でのみ機能します。Treyの答えが望ましい解決策だと思います。
複数のネストされたディレクトリをパスに結合したかったのです。もともと私は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"
この質問は2010年に出されたものですが、執筆時点では「elispでファイルパスを結合する」などの検索でトップヒットだったので、回答を更新したいと思いました。
2010年以来、Emacsの世界では物事が大きく変化しています。これは、以下の回答で簡単に言及されているため、多少重複した回答ですが、少し具体化していきます。ファイルインタラクション専用のライブラリがありますf.el
:
@magnarsの優れたs.elとdash.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で利用できるはずです。
便利なファイルとディレクトリの操作ライブラリを使用する場合は、必要なのは。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より前には存在しなかったので、EmacsLispで文字列がサフィックスで終わっているかどうかを確認するで自分で書いた。
これが私が使用するものです:
(defun catdir (root &rest dirs)
(apply 'concat (mapcar
(lambda (name) (file-name-as-directory name))
(push root dirs))))
@dbrとの違い:
root
(注を参照)root
として扱いますが、ルートとして始まる最初のjoindirs
コンポーネントを使用します。"/"
ノート
多くのファイル処理関数(すべて、ほとんど、???)は、冗長なスラッシュを正規化expand-file-name
し、相対パスで(または同様の)呼び出しを行うため、#2と#3は実際には重要ではない場合があります。
Emacsのマニュアルへのリンクで前に言ったことを完了するためだけに:
他の人が以前に言ったように、OPの質問への答えはを使用することexpand-file-name
です。これは組み込み関数であり、Cで実装されているため、外部ライブラリを使用する必要はありません。
これは、 「ファイル名を拡張する関数」というタイトルのEmacsLispマニュアルのセクションで説明されています。
そして、Emacsのオンラインヘルプによると、この機能はバージョン... 1.6のEmacsで導入されました!だから...それは利用できるはずです!
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"