如何在Emacs中重命名已打开的文件?

278

在Emacs中,是否有一种方法可以在查看文件时重命名它?类似于另存为,但原始文件应该消失。

15个回答

393

是的,使用dired模式,您可以:

  • C-x d 打开dired
  • RET 选择当前文件所在目录
  • C-x C-j (dired-jump 到Dired中当前文件的名称)
  • R 重命名文件(或使用dired-do-rename)。
  • q 返回(重命名后的)文件缓冲区

重命名相当于shell的mv,但它还会更新任何打开的缓冲区,并且与mv不同,它不会更改文件在文件系统上的访问和修改时间。


15
这并不是直接重命名当前文件。 - pupeno
5
使用C-x b,你就可以回到原始缓冲区。你可以编写一个Elisp函数来完成这个操作,但我怀疑它能为你节省很多按键次数。 - Chris Conway
6
除了使用C-x b之外,你还可以按C-x k回到原始缓冲区。 - Yoo
37
默认情况下,我的Emacs中未绑定C-x C-j快捷键。首先执行M-x load-library RET dired-x RET会使其绑定。 - ntc2
5
如果 C-x C-j 没有被绑定的话,另一个选择是在第一次使用时执行 M-x dired-jump。这将自动加载 dired-x(也会定义从此处开始的 C-x C-j)。 - Fernando Basso
显示剩余5条评论

132

仅为完整性考虑,因为有些人可能会访问此页面以期望得到有关Emacs的“另存为”功能的答案,那么对于已经打开的文件,可以使用C-x C-w。


14
@asmeurer 你错了!保存后,你将编辑新文件。 - Joel G Mathew
3
为什么没有一个可以踩评论的功能?请问您能解释一下吗? - bearfriend
8
也许asmeurer的意思是,“你正在编辑的文件仍将存在”。那么,这样说是否正确呢?我想去查看,但是这样你就不能给我的评论点踩了,哈哈哈。 - Brady Trainor
然后抹掉旧的? - Felipe
保存不等于重命名。 - RichieHH
显示剩余2条评论

95

尝试使用Steve Yegge的.emacs文件中提供的这个函数:

;; 来源:http://steve.yegge.googlepages.com/my-dot-emacs-file
(defun rename-file-and-buffer (new-name)
  "将当前缓冲区和其访问的文件同时重命名为NEW-NAME。"
  (interactive "s新名称: ")
  (let ((name (buffer-name))
        (filename (buffer-file-name)))
    (if (not filename)
        (message "缓冲区 '%s' 没有访问任何文件!" name)
      (if (get-buffer new-name)
          (message "名为 '%s' 的缓冲区已经存在!" new-name)
        (progn
          (rename-file filename new-name 1)
          (rename-buffer new-name)
          (set-visited-file-name new-name)
          (set-buffer-modified-p nil))))))

查看该页面,还有另一个非常有用的相关函数称为“move-buffer-file”。


1
注意:此方法与(setq uniquify-buffer-name-style 'forward)不兼容,这意味着如果您有一个名为users\index.html的缓冲区(因为您已经有另一个缓冲区用于posts\index.html),重命名将失败。 - Ev Dolzhenko
“(set-buffer-modified-p nil)” 似乎是不必要的。如果您在修改的缓冲区上调用了“rename-file-and-buffer”,然后尝试杀死它,它将会毫不犹豫地执行而不警告您有未保存的更改。 - roldugin
这个函数还会(有点烦人地)在检查当前缓冲区是否与文件关联(如果是,则中止)之前要求您提供一个新名称。 - Thomas

33

我最喜欢的是Magnars(来自Emacs Rocks screencasts)的那个。

和其他替代品不同,你不必从头开始键入名称-你可以修改当前名称。

(defun rename-current-buffer-file ()
  "Renames current buffer and file it is visiting."
  (interactive)
  (let* ((name (buffer-name))
        (filename (buffer-file-name))
        (basename (file-name-nondirectory filename)))
    (if (not (and filename (file-exists-p filename)))
        (error "Buffer '%s' is not visiting a file!" name)
      (let ((new-name (read-file-name "New name: " (file-name-directory filename) basename nil basename)))
        (if (get-buffer new-name)
            (error "A buffer named '%s' already exists!" new-name)
          (rename-file filename new-name 1)
          (rename-buffer new-name)
          (set-visited-file-name new-name)
          (set-buffer-modified-p nil)
          (message "File '%s' successfully renamed to '%s'"
                   name (file-name-nondirectory new-name)))))))

感谢James Yang提供正确版本。


这个程序没有正常工作。这个会在文件末尾添加一个“/”。 - mythicalcoder
2
谢谢你为我指出这个问题。SO的做法是纠正答案,而不是发布新的、稍作修改的答案。 - The Unfun Cat

15

以下是一个更健壮的版本,改编自stevey。

;; Originally from stevey, adapted to support moving to a new directory.
(defun rename-file-and-buffer (new-name)
  "Renames both current buffer and file it's visiting to NEW-NAME."
  (interactive
   (progn
     (if (not (buffer-file-name))
         (error "Buffer '%s' is not visiting a file!" (buffer-name)))
     ;; Disable ido auto merge since it too frequently jumps back to the original
     ;; file name if you pause while typing. Reenable with C-z C-z in the prompt.
     (let ((ido-auto-merge-work-directories-length -1))
       (list (read-file-name (format "Rename %s to: " (file-name-nondirectory
                                                       (buffer-file-name))))))))
  (if (equal new-name "")
      (error "Aborted rename"))
  (setq new-name (if (file-directory-p new-name)
                     (expand-file-name (file-name-nondirectory
                                        (buffer-file-name))
                                       new-name)
                   (expand-file-name new-name)))
  ;; Only rename if the file was saved before. Update the
  ;; buffer name and visited file in all cases.
  (if (file-exists-p (buffer-file-name))
      (rename-file (buffer-file-name) new-name 1))
  (let ((was-modified (buffer-modified-p)))
    ;; This also renames the buffer, and works with uniquify
    (set-visited-file-name new-name)
    (if was-modified
        (save-buffer)
      ;; Clear buffer-modified flag caused by set-visited-file-name
      (set-buffer-modified-p nil)))

  (setq default-directory (file-name-directory new-name))

  (message "Renamed to %s." new-name))

不错啊。现在我正在我的functions.el文件中轻松愉悦。 - Felix D.

13
这是另一种版本,非常强大且支持VC(Visual C++):
(defun rename-file-and-buffer ()
  "Rename the current buffer and file it is visiting."
  (interactive)
  (let ((filename (buffer-file-name)))
    (if (not (and filename (file-exists-p filename)))
        (message "Buffer is not visiting a file!")
      (let ((new-name (read-file-name "New name: " filename)))
        (cond
         ((vc-backend filename) (vc-rename-file filename new-name))
         (t
          (rename-file filename new-name t)
          (set-visited-file-name new-name t t)))))))

你可以在这里了解更多相关的it技术内容。

11

2
我刚在Doom Emacs(v27.1)中测试了这个,它也可以正常工作。谢谢。 - flarkmarup

9

1
这会产生(不一定是理想的)副作用,将旧文件和新文件添加到要提交的版本控制中。但是+1,因为它使用内置函数来(大多数)回答了OP的问题。 - dinosaur
即使在今天,它也可以与GNU Emacs 25.3.2 (x86_64-pc-linux-gnu,GTK+版本3.18.9)一起使用。 - agent18
1
使用 vc-rename-file 命令时出现了这个提示,请在移动文件之前更新它们。 - agent18
确实,这是一个不错的解决方案,但它并不总是允许你进行重命名。 - pglpm

6

Emacs 26.3 (2020-04-03)引入了rename-file函数,可通过M-x rename-file命令重命名当前文件或其他文件。


2
在Emacs 25中,这不会更改缓冲区中的文件名,即下一次写入将再次进入旧文件名。 - Christian Herenz
emacs 27中,我做了这个操作,但它只起到了“另存为”的作用。 - Danh Le

5

基于magnars版本,我进行了如下修改,修复了INIT文件名的部分:

(defun rename-current-buffer-file ()
  "Renames current buffer and file it is visiting."
  (interactive)
  (let* ((name (buffer-name))
        (filename (buffer-file-name))
        (basename (file-name-nondirectory filename)))
    (if (not (and filename (file-exists-p filename)))
        (error "Buffer '%s' is not visiting a file!" name)
      (let ((new-name (read-file-name "New name: " (file-name-directory filename) basename nil basename)))
        (if (get-buffer new-name)
            (error "A buffer named '%s' already exists!" new-name)
          (rename-file filename new-name 1)
          (rename-buffer new-name)
          (set-visited-file-name new-name)
          (set-buffer-modified-p nil)
          (message "File '%s' successfully renamed to '%s'"
                   name (file-name-nondirectory new-name)))))))

这个更好。上面给出的Magnars版本不能正常工作。在末尾添加一个“/”字符。 - mythicalcoder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接