对文件名进行展开(expanding),是指将相对文件名转换为绝对文件名。 由于这一过程是相对于某个默认目录完成的, 你必须同时指定默认目录与待展开的文件名。 展开还包括对 ~/ 这类缩写的解析 (see abbreviate-file-name), 以及消除 ./ 和 name/../ 这类冗余路径。
该函数将 filename 转换为绝对文件名。
若提供了 directory,则当 filename 为相对路径且不以 ‘~’ 开头时,
以此作为起始默认目录。
(directory 本身应为绝对目录名或目录文件名;可以 ‘~’ 开头。)
否则使用当前缓冲区的 default-directory。例如:
(expand-file-name "foo")
⇒ "/xcssun/users/rms/lewis/foo"
(expand-file-name "../foo")
⇒ "/xcssun/users/rms/foo"
(expand-file-name "foo" "/usr/spool/")
⇒ "/usr/spool/foo"
若 filename 中第一个斜杠前的部分为 ‘~’,
会被展开为你的主目录,通常由环境变量 HOME 指定
(see General Variables in The GNU Emacs Manual)。
若第一个斜杠前为 ‘~user’ 且 user 为有效登录名,
则展开为该用户的主目录。
如果你不希望对可能以字面量 ‘~’ 开头的相对文件名进行展开,
可以使用 (concat (file-name-as-directory directory) filename)
代替 (expand-file-name filename directory)。
包含 ‘.’ 或 ‘..’ 的文件名会被简化为规范形式:
(expand-file-name "bar/../foo")
⇒ "/xcssun/users/rms/lewis/foo"
某些情况下,开头的 ‘..’ 组件可能会保留在结果中:
(expand-file-name "../home" "/")
⇒ "/../home"
这是为了兼容那些在根目录 / 之上还有超级根概念的文件系统。 在其他文件系统中,/../ 与 / 解析效果完全一致。
展开 . 或空字符串会返回默认目录:
(expand-file-name "." "/usr/spool/")
⇒ "/usr/spool"
(expand-file-name "" "/usr/spool/")
⇒ "/usr/spool"
注意:expand-file-name 不会 展开环境变量,
只有 substitute-in-file-name 会执行该操作:
(expand-file-name "$HOME/foo")
⇒ "/xcssun/users/rms/lewis/$HOME/foo"
另外,expand-file-name 在任何层级都不会跟随符号链接。
这导致 file-truename 与 expand-file-name
对 ‘..’ 的处理方式存在差异。
假设 ‘/tmp/bar’ 是指向目录 ‘/tmp/foo/bar’ 的符号链接,则结果如下:
(file-truename "/tmp/bar/../myfile")
⇒ "/tmp/foo/myfile"
(expand-file-name "/tmp/bar/../myfile")
⇒ "/tmp/myfile"
如果你需要在 ‘..’ 之前跟随符号链接,
应确保直接调用 file-truename,
而不事先直接或间接调用 expand-file-name。See 真实路径。
这个缓冲区局部变量的值为当前缓冲区的默认目录。 它应为绝对目录名,可以 ‘~’ 开头。 该变量在每个缓冲区中都是局部有效的。
当 expand-file-name 的第二个参数为 nil 时,
会使用该默认目录。
其值始终是以斜杠结尾的字符串。
default-directory
⇒ "/user/lewis/manual/"
该函数将 filename 中的环境变量引用替换为对应变量值。 遵循标准 Unix Shell 语法,‘$’ 为环境变量替换前缀。 若输入中包含 ‘$$’,会被转换为 ‘$’, 用户可通过这种方式对 ‘$’ 进行转义。
环境变量名是 ‘$’ 之后的一串字母、数字(包含下划线)。 若 ‘$’ 后紧跟 ‘{’, 则变量名为从该位置到匹配的 ‘}’ 之间的内容。
对已经经过 substitute-in-file-name 处理的结果
再次调用该函数,通常会产生错误。
例如,用于转义的 ‘$$’ 无法正常工作,
且环境变量值中的 ‘$’ 可能引发重复替换。
因此,若程序调用该函数后,输出会再次被传入本函数,
需要将所有 ‘$’ 加倍,以避免后续错误。
这里假设保存用户主目录的环境变量 HOME
值为 ‘/xcssun/users/rms’。
(substitute-in-file-name "$HOME/foo")
⇒ "/xcssun/users/rms/foo"
替换完成后,若 ‘~’ 或 ‘/’ 紧跟在另一个 ‘/’ 之后, 函数会丢弃其之前的所有内容(直到紧邻的前一个 ‘/’)。
(substitute-in-file-name "bar/~/foo")
⇒ "~/foo"
(substitute-in-file-name "/usr/local/$HOME/foo")
⇒ "/xcssun/users/rms/foo"
;; /usr/local/ has been discarded.
某些场景下并不希望展开文件名。 此时可以对文件名进行引用,阻止展开并按字面量处理。 引用方式为在文件名前添加前缀 ‘/:’。
该宏为文件名 name 添加引用前缀 ‘/:’。 对于本地文件名 name,直接在前方添加 ‘/:’。 若 name 为远程文件名,则对其本地部分(see 实现“魔法”文件名机制)进行引用。 若 name 已是被引用的文件名,则直接原样返回。
(substitute-in-file-name (file-name-quote "bar/~/foo"))
⇒ "/:bar/~/foo"
(substitute-in-file-name (file-name-quote "/ssh:host:bar/~/foo"))
⇒ "/ssh:host:/:bar/~/foo"
该宏无法用于阻止魔法文件名的文件名处理器生效(see 实现“魔法”文件名机制)。
该宏从文件名 name 中移除引用前缀 ‘/:’(如果存在)。 若 name 为远程文件名,则对其本地部分取消引用。
若 name 带有前缀 ‘/:’ 被引用,该宏返回非 nil。
若 name 为远程文件名,则检查其本地部分。