记录学习

2008年7月12日星期六


dired 是 Emacs 自带的文件管理器,操作非常方便,再加上一些扩展之后无疑是 一个理想的文件管理器。看看这里来了解如何增强你的 dired 。
Mark & Flag

dired 最方便的一点就是可以对许多文件进行标记,并进行批量操作。标记的方 法有很多,最普通的标记就是 d 为当前文件贴上删除标签,之后可以使用 x 来 真正删除所有贴上删除标签的文件。

dired 还提供了许多预定义的方便的标记操作(当使用 C-u 传递一个前缀参数 时,他们执行相反操作,即去掉标记),例如:
# 为所有自动保存的文件(通常是文件名开始和结尾都是 # 的文件)贴上删除 标签。
~ 为所有备份文件(即文件名以 ~ 结尾的文件,Emacs 和 vi 等编辑器默认 情况下都会产生这样的文件)贴上删除标签。
& 为“垃圾文件”(看 dired-garbage-files-regexp 的值可以知道 dired 把 哪些文件当作了垃圾文件)贴上删除标签。

通常这些命令可以方便地帮你清理垃圾,如果还不满意,可以使用 % d REGEXP <RET> 来输入自己的正则表达式,匹配到的文件会被贴上删除标签。

当然,能用的标签并不止是 D (即删除标签),几乎任何一个字符都可以使用,不 过最常用的还是 * , m 命令即是以 * 标记当前文件。同样,dired 提供了很多 方便的标记操作(这些命令在传递一个前缀参数的时候都会执行相反的操作,例如 C-u * * 会去掉所有可执行文件的标记):
* * 标记所有可执行文件。
* @ 标记所有符号链接。
* / 标记所有目录(不包括 . 和 .. )。
* s 标记所有文件(不高考 . 和 .. )。
* . 标记具有给定扩展名的文件。
% m REGEXP <RET> 或 * % REGEXP <RET> 标记所有匹配到给定的正则表达式 的文件。
% g REGEXP <RET> 标记所有文件 内容 匹配到给定的正则表达式的文件。

另外,还有一些相关的命令:
u 去除当前行的标记。
<DEL> 上移一行并去除该行的标记。
U 去除所有标记。
* ? MARKCHAR 或 M-<DEL> 去除所有以 MARKCHAR 标记的文件的标记,如果 传递一个前缀参数,则会对每一个文件要求你确认是否去除标记。
t 交换标记,即所有原来标记为 * 的文件被置于未标记状态,原来未标记的 文件被标记为 * ,原来有其他标记的文件不受影响。

上面的操作都是使用 * 进行标记,但是 dired 可以使用更多的字符进行标记, 只是没有提供相应的快捷键操作而已,你可以先以 * 标记,然后使用 * c OLD-MARKCHAR NEW-MARKCHAR 来把 * 标记变换成其他标记,几乎任何字符(当然 不包括中文这种多字节的字符)都可以作为标记,不过空格被特殊对待,用于表示 所有未标记的文件。

列举了这么多命令,多少有些枯燥,作为一个例子,我们来把当前目录下的所有 备份文件移动到 ~/backup 目录下。假设当前目录已经有一些文件被你以 D 标 记,但是暂时还不想删除:
选择个临时标记,比如 t ,只要保证当前 buffer 里面没有已经存在的 这种标记就行了。
* c D t 把当前所有 D 标签换为 t 标签。
~ 以 D 标记所有备份文件。
* c D * 把 D 标签换为 * 标签。
R ~/backup <RET> 来把所有标记为 * 的文件移动到 ~/backup 目录里面。
* c t D 恢复原来的 D 标记。

当然这要假设你原来没有设定其他的 * 标记,要不然你也可以再添加一个临时 标记。总之操作和清晰也很方便,感觉像在汇编语言里面使用寄存器一样,大多 数批量操作都是针对 * 标记的,所以对某个标记操作之前需要把他先转换为 * 标记。

另外,还有一个非常强大的标记的方法,绑定到 M-( 或 * ( 上。它可以让你使 用断言来决定标记哪些文件。 C-h f dired-mark-sexp RET 可以得到详细的文档。 这个功能非常强大,有点类似于 find 程序,例如,标记所有没有编译的Elisp 文件(如果编译了,那么会有一个同名,但是扩展名为 .elc 的文件存在) 的方法 是输入这个断言: (and (string-match "\.el$" name) (not (file-exists-p (concat name "c")))) 。
文件操作

dired 内建了很多文件操作,对于操作的文件有一个统一的约定,按照顺序是:
如果你通过 C-u 传递一个前缀参数 N ,那么它对从当前行开始的 N 行执 行操作( N 也可以是负数)。
如果有被标记为 * 的文件,则以这些文件为操作对象。
只对当前光标所在的文件进行操作。
常用操作

这些命令全部绑定到大写字母上,记忆也非常方便:
C 拷贝文件。把 dired-recursive-copies 设为非 nil 的值可以递归拷贝目 录,通常我们设定为 top ,这表示对于顶层目录 dired 会先进行询问是否 要递归拷贝,而其中的子目录则不再询问。如果嫌询问太麻烦,可以直接设 置为 always 。
D 删除文件。类似的有一个 dired-recursive-deletes 变量可以控制递归删 除。
R 重命名文件,也就是移动文件。
H 创建硬链接。
S 创建软链接。
M 修改权限位,即 shell 里面的 chmod 命令。
G 修改文件所属的组。
O 修改文件的所有者。
T 修改文件的修改时间,类似于 shell 命令 touch 。
P 打印文件。
Z 压缩或解压文件。
L 把 Elisp 文件加载进 Emacs 。
B 对 Elisp 文件进行 Byte compile 。
A 对文件内容进行正则表达式搜索,搜索会在第一个匹配的地方停下,然后 可以使用 M-, 搜索下一个匹配。
Q 对文件内容进行交互式的正则表达式替换。
shell 命令

除了这些操作,还可以使用 ! 来执行 shell 命令。这里介绍了自动猜测 shell 命令的办法,就类似于通常的文件管理器里面以关联的程序打开了。
强大的重命名功能

dired 有一个文件名转换的理念,所以转换,并不一定是重命名,还可以是复制 和创建链接。所以,除了 % u 和 % l 重命名原文件为大写、小写外,一个使用 正则表达式进行转换的命令提供了四个选项: % X 其中 X 可以是 R , C , H 和 S ,分别代表重命名、复制、创建硬链接和创建软链接,他们使用匹配和替换的 机制,这有点像 rename 这个程序,例如: % R .[^.]*$ <RET> .1& <RET> 给原来的文件名加个标号 1 ,把 foo.txt 变成 foo.1.txt 。

另外,dired 还有一个叫做 Wdired 的扩展可以直接在 dired 的 buffer 里面编 辑文件名来达到重命名的效果。使用 M-x wdired-change-to-wdired-mode 进入 编辑模式,这个时候可以直接像编辑普通文本一样编辑文件名,还可以添加路径 来实现把文件移动到其他目录。除了文件名可以编辑以外,其他部分被标记为只 读,但是如果把 wdired-allow-to-change-permissions 设为 t 的话,还可以 编辑文件的权限位。编辑完成之后使用 C-c C-c 来应用所做的编辑。非常方便。
排序和过滤

dired 有方便的排序功能,这里介绍了如何方便地使用排序功能。另外 dired 还有一个 k 用于去掉不想显示出来的文件,它并不删除磁盘上的文件,只是临 时从 dired 的 buffer 中去掉他们, g 刷新一下它们又会显示出来,这样,首 先用强大的标记功能进行标记,然后使用 k 去掉,就实现了过滤的功能。
子目录操作

dired 允许同时操作当前目录和子目录。在 dired-listing-switches 里面加入 R 选项就可以显示子目录,如果只是想临时显示某个子目录的内容,对该目录执 行 i 操作就会把该子目录的内容添加到 dired 当前 buffer 的末尾并把光标移 动到那里,dired 在移动之前会先设置一个 mark ,所以可以使用 C-u C-<SPC> (对于我来说,我把 set-mark-command 绑定到了 M-<SPC> 上,这里自然就是使 用 C-u M-<SPC> 了)回到原来的位置。

关于子目录的更多方便的操作可以参见 Emacs 的 info 文档。
其他功能

还有一些方便的功能,我把几个常用的命令列在这里:
+ 创建目录
w 复制文件名,如果通过 C-u 传递一个前缀参数 0 ,则复制决定路径名, 如果只是 C-u 则复制相对于 dired 当前目录的相对路径。
I 把当前文件以 info 文档的格式打开。
N 把当前文件以 man 格式打开(使用 WoMan)。

Y 为所有标记的文件创建一个到指定目录的相对符号连接(即使用相对路径进 行引用,而不是绝对路径)。

Contents
dired 扩展列表 
使用单个 buffer 
不用任何扩展 
对内置的函数进行 advice 
使用 dired+.el 
使用 dired-single.el 
跳转到当前 buffer 对应文件所在目录 
使用 dired-x.el 
使用 dired-single.el 
快速定位到某个文件 
使用 dired-details.el 和 dired-details+.el 
使用 dired-view.el 
使用 dired-isearch.el 
忽略不感兴趣的文件 
使用 dired-x.el 
使用 dired+.el 
以合适的程序打开文件 
使用 dired-x.el 
使用 run-assoc.el 
使用 trivial-mode.el 
排序 
使用 dired-sort-menu.el 和 dired-sort-menu+.el 
自己对 dired 进行扩展 
使用 dired 自身的功能 
dired 扩展列表

这里列举一下我们后面要用到的 dired 的扩展,他们都是单个 Elisp 文件,安 装方法都可以在文件头部的注释部分找到。
dired-single.el 让 dired 只使用一个 buffer 。
dired+.el 扩展 dired.el 、 dired-x.el 和 dired-aux.el 的功能。
dired-x.el 这是 Emacs 自带的扩展,安装方法可以查看 Emacs 的 info 文 档。
dired-isearch.el 在 dired 里面只针对文件名做 i-search 。
dired-details.el 隐藏多余的信息,显示简洁的文件列表。
dired-details+.el dired-details.el 的扩展。
dired-view.el 输入文件名的首字母快速跳转。
dired-sort-menu.el 方便地使用各种方式进行排序,并提供一个菜单用于执 行操作。
dired-sort-menu+.el dired-sort-menu.el 的扩展。
使用单个 buffer

dired 在每打开一个新的目录之后总会打开一个新的 buffer ,这样,在浏览了 很多目录之后,Emacs 里面就会充满了目录的 buffer ,很不舒服,不过有很多 方法可以解决这个问题。
不用任何扩展

在 Emacs 22 里面,可以使用 a 来访问目录,这样可以避免打开多余的buffer ,不过使用回车或者鼠标点击打开目录的话,还是会产生多出来的 buffer 。
对内置的函数进行 advice

可以通过对 Emacs 内置的函数进行 advice 也可以达到这个目的,其实就是对 进入其他目录的动作进行截获,判断如果进入了一个目录,则把原来目录的那个 buffer 删掉:
(defadvice dired-find-file (around dired-find-file-single-buffer activate)
  "Replace current buffer if file is a directory."
  (interactive)
  (let ((orig (current-buffer))
  (filename (dired-get-file-for-visit)))
  ad-do-it
  (when (and (file-directory-p filename)
  (not (eq (current-buffer) orig)))
  (kill-buffer orig))))
(defadvice dired-up-directory (around dired-up-directory-single-buffer activate)
  "Replace current buffer if file is a directory."
  (interactive)
  (let ((orig (current-buffer)))
  ad-do-it
  (kill-buffer orig)))
使用 dired+.el

dired+.el 提供 toggle-dired-find-file-reuse-dir ,用于打开和关闭默认按 键是否重用当前 buffer ,如果你不想影响默认新闻,它还提供了了两个函数, diredp-find-file-reuse-dir-buffer 和 diredp-mouse-find-file-reuse-dir-buffer ,你可以把他们绑定到其他的按键 上面。不过,使用 ^ 跳到上一级目录的时候仍然是没有重用当前的 buffer 的,当 然,你可以使用在 .. 上按回车跳到上一级的办法。
使用 dired-single.el

dired-single.el 专门为这个问题而设计,他可以保证所有的 dired buffer 都 使用同一个名字,换句话说,总是只会有一个 dired buffer 存在,并且我们可 以方便地跳到那个 buffer ,如果 buffer 不存在的话, dired-single.el 会自 动帮我们创建。

作为基本使用,在 dired 里面重新绑定 回车、鼠标点击和 ^ 键:
(require 'dired-single)
(add-hook 'dired-mode-hook
  (lambda ()
  (define-key dired-mode-map (kbd "RET") 'joc-dired-single-buffer)
  (define-key dired-mode-map (kbd "<mouse-1>") 'joc-dired-single-buffer-mouse)
  (define-key dired-mode-map (kbd "^")
  (lambda ()
  (interactive)
  (joc-dired-single-buffer "..")))))

另外,如果想使用单一的 dired buffer 模式,首先要固定住 dired buffer 的 名字,这样才能方便地在任何时候跳转到原来存在的 dired buffer 那里。定制 joc-dired-magic-buffer-name 可以设定这个值,他的默认值为 *dired* 。然后 要把 joc-dired-use-magic-buffer 设定为 t 以启用 magic-buffer 机制。最后 我们绑定 C-x d 到 joc-dired-magic-buffer 上,这样,就可以使用 C-x d 来 跳转回存在的 dired buffer ,如果事先没有这个 buffer 存在,他会询问你要 访问的目录并自动创建这个 buffer 。
(require 'dired-single)
(add-hook 'dired-load-hook
  (lambda ()
  (define-key dired-mode-map (kbd "RET") 'joc-dired-single-buffer)
  (define-key dired-mode-map (kbd "<mouse-1>") 'joc-dired-single-buffer-mouse)
  (define-key dired-mode-map (kbd "^")
  (lambda ()
  (interactive)
  (joc-dired-single-buffer "..")))
  (setq joc-dired-use-magic-buffer t)
  (setq joc-dired-magic-buffer-name "*dired*")))
(global-set-key (kbd "C-x d")
  'joc-dired-magic-buffer)

但是由于 dired-single 的实现方法的原因,现在不能在 .. 上回车跳到上一级 目录了,不过这不是什么大问题,可以使用 ^ 键直接跳到上一级目录。但是如 果觉得不爽,可以把这个 patch 打到 dired-single.el 上去以修复这个问题:
*** dired-single.el 2006-10-02 23:47:52.000000000 +0800
--- dired-single.el.new 2006-10-03 14:19:42.000000000 +0800
***************
*** 244,250 ****
  its name will not change every time a new directory is entered)."
  (interactive)
  ;; use arg passed in or find name of current line
! (let ((name (or default-dirname (dired-get-filename))))
  (save-excursion
  (save-match-data
  ;; See if the selection is a directory or not.
--- 244,250 ----
  its name will not change every time a new directory is entered)."
  (interactive)
  ;; use arg passed in or find name of current line
! (let ((name (or default-dirname (dired-get-file-for-visit))))
  (save-excursion
  (save-match-data
  ;; See if the selection is a directory or not.

另外, dired-single.el 是根据 dired 列出的文件类型那个 d 来判断是否是 目录的,这样对于处理软链接的目录就不行了,我们可以使用 Emacs 提供的 file-directory-p 来判断,这个函数会跟踪软链接,只要把这个 patch 打到 dired-single.el 上就可以解决这个问题:
*** dired-single.el 2006-10-05 11:17:06.000000000 +0800
--- dired-single.el.new 2006-10-05 11:17:06.000000000 +0800
***************
*** 252,258 ****
  (let ((eol (point)))
  (beginning-of-line)
  ;; assume directory if arg passed in
! (if (or default-dirname (re-search-forward "^ d" eol t))
  ;; save current buffer's name
  (let ((current-buffer-name (buffer-name)))
  ;; go ahead and read in the directory
--- 252,258 ----
  (let ((eol (point)))
  (beginning-of-line)
  ;; assume directory if arg passed in
! (if (or default-dirname (file-directory-p name))
  ;; save current buffer's name
  (let ((current-buffer-name (buffer-name)))
  ;; go ahead and read in the directory
跳转到当前 buffer 对应文件所在目录
使用 dired-x.el

dired-x.el 提供了两个函数可以用于跳转到当前 buffer 所对应的文件的目录:
(define-key global-map (kbd "C-x C-j") 'dired-jump)
(define-key global-map (kbd "C-x 4 C-j") 'dired-jump-other-window)
使用 dired-single.el

既然 dired-single.el 可以让 dired 使用单一的 buffer ,并随时方便跳转到 那个 buffer ,我们也可以稍微扩展一下让它能做到跳转到当前 buffer 说对应 的文件的目录。
(global-set-key (kbd "C-x d")
  'joc-dired-magic-buffer)
(global-set-key (kbd "C-x 4 d")
  (lambda (directory)
  (interactive "D")
  (let ((win-list (window-list)))
  (when (null (cdr win-list)) ; only one window
  (split-window-vertically))
  (other-window 1)
  (joc-dired-magic-buffer directory))))
快速定位到某个文件
使用 dired-details.el 和 dired-details+.el

这两个扩展提供把 dired buffer 里面除了文件名以外的其他信息隐藏起来的功 能,隐藏起来之后可以做普通的 i-search ,就能达到只搜索文件的功能。
使用 dired-view.el

dired-view.el 提供一个方便的功能,在打开 dired-view-minor-mode 之后, 可以输入文件名的首字母快速定位到文件。通常这个 minor-mode 并不是一直打 开,事实上,他是重新定义了所有的字母以及数字键说绑定的函数,所以如果文 件名是中文的话也没有办法,而且按键会和 dired 本身的很多按键冲突,于是 他提供了方便的打开和关闭的函数:
(require 'dired-view)
(define-key dired-mode-map (kbd ";") 'dired-view-minor-mode-toggle)
使用 dired-isearch.el

dired-isearch.el 让你能够在 dired 里面使用只对文件名部分执行 i-search ,非常方便:
(require 'dired-isearch)
(define-key dired-mode-map (kbd "C-s") 'dired-isearch-forward)
(define-key dired-mode-map (kbd "C-r") 'dired-isearch-backward)
(define-key dired-mode-map (kbd "M-C-s") 'dired-isearch-forward-regexp)
(define-key dired-mode-map (kbd "M-C-r") 'dired-isearch-backward-regexp)

可是这个东西并不能和 dired+.el 共同工作,默认情况下,dired 在文件显示 的地方可以用鼠标进行点击,而 dired-isearch.el 也正是依靠这个进行判断 的,可是 dired+.el 让整行都处于可用鼠标点击进行操作(事实上, dired+.el 提供了很强大的鼠标操作功能,例如,可以像常见的文件管理器那样区域选择、 用 Shift 加鼠标来选择以及用 Ctrl 加鼠标来选择等), dired-isearch.el 的 方法就不管用了,不过其实只要一点小小的改动,把 dired-isearch.el 里面的 所有的 help-echo 改为 dired-filename 就可以正常使用了。
忽略不感兴趣的文件
使用 dired-x.el

dired-x.el 允许忽略不感兴趣的文件,不让他们显示出来,并且可以使用 M-o 来方便地切换忽略与显示。有两个变量可以控制究竟忽略哪些文件:
dired-omit-files 是一个正则表达式,凡是匹配这个正则表达式的文件就会 被忽略掉,默认情况下会忽略自动保存的文件(以 # 开头),当前目录和父目 录(也就是 . 和 .. )。
dired-omit-extensions 是一个字符串列表,凡是文件名结尾是这个列表中 的某一个的时就会被忽略。

我通常把 dired-omit-files 设置为 "^#\|^\..*" ,这样可以忽略掉以 . 开 头的文件,在 Linux 下面这通常表示隐藏文件, ls 默认情况下也不会显示他 们,而且主目录下面通常有一大堆这样的文件,显示出来看起来非常恐怖。
(add-hook 'dired-mode-hook
  (lambda ()
  (setq dired-omit-files "^#\|^\..*") ; omit all hidden file which starts with `.'
  (dired-omit-mode 1))) ; initially omit unintrested files
使用 dired+.el

事实上 dired+.el 这里提供了用一种“让你觉得不重要”的颜色来显示不感兴趣的 文件的功能,他们由 dired-omit-extensions 和 completion-ignored-extensions 这两个变量来控制。

以合适的程序打开文件

在 Windows 上,文件相关联的程序都是在系统那里注册了的,可以直接执行相 关程序而不用自己再去定义关联列表。我没有在 Windows 下用这个,所以没有 办法做什么测试,也不多说,EmacsWiki 上有比较详细的介绍。这里我说一下几 种在 Linux 下实现使用关联的程序打开文件的方法,都是需要自己定义关联关 系的,因为 Linux 下并没有统一的关联程序的标准,而且,如果只是使用 dired 作为文件管理器的话,自己定义一些自己最常用的关联关系也是很方便的。
使用 dired-x.el

dired-x.el 提供猜测 shell command 的功能,在某一个文件上按 ! , dired-x.el 会把猜测的命令在方括号中显示出来,可以直接回车执行,也可 以用 M-p 来取得默认命令,并进行适当的编辑再执行,如果有几个可选的默认 命令,可以用 M-p 、 M-n 来进行轮换选择。

dired-x.el 根据 dired-guess-shell-alist-default 变量里面定义的命令来进 行猜测,另外还有一个 dired-guess-shell-alist-user 用于自定义猜测的命 令,这里面的命令会覆盖掉 dired-guess-shell-alist-default 里面的命令。

这个变量是一个列表,表的每一项格式为 (REGEXP COMMAND ...) ,其中每一个 COMMAND 都可以是一个字符串或者是一个可以求值得到一个字符串的 Elisp 表 达式。在字符串里面如果有 * 出现则会被替换成文件名,另外,也可以直接在 Elisp 表达式里面使用 file 这个变量,查看 dired-guess-shell-alist-default 就可以得到很多例子,例如:
("\.t\(ar\.\)?gz$"
 (concat "mkdir "
  (file-name-sans-extension file)
  "; " dired-guess-shell-gnutar " -C "
  (file-name-sans-extension file)
  " -zxvf")
 (concat "mkdir "
  (file-name-sans-extension file)
  "; gunzip -qc * | tar -C "
  (file-name-sans-extension file)
  " -xvf -"))

不足的地方是只能对单个文件进行猜测,如果 mark 了一堆文件的话,这个功能 就不太好用了。

另外,对于 X 下的应用程序,我们通常不希望它把 Emacs 阻塞掉,而是同步执 行,只需要在末尾加上 & 即可同步执行,同时 Emacs 会收集程序输出,例如:
("\.[rR][mM]\(?:[vV][bB]\)?" "mplayer * &")

可是有些程序的输出含有很多终端控制字符,mplayer 就是一个例子,我在这样 运行 mplayer 的时候显得十分卡,我想可能是输出被 Emacs 捕获到 buffer 里 面的原因。这些输出本身就没有什么用,如果还会让程序运行缓慢的话,就更可 恶了。

一个办法是直接在 shell 命令那里下手,例如:
("\.[rR][mM]\(?:[vV][bB]\)?" "mplayer * >/dev/null 2>&1 &")

就不会因为大量输出而阻塞了,不过这样仍然会打开一个空的叫做 *Async Shell Command* 的 buffer 。另外一个办法是修改代码,把所有以 & 结尾的 后台程序的输出都直接丢弃掉。
(defadvice dired-run-shell-command (around kid-dired-run-shell-command (command))
  "run a shell command COMMAND .
If the COMMAND ends with `&' then run it in background and *discard* the
output, otherwise simply let the original `dired-run-shell-command' run it."
  (if (string-match "&[[:blank:]]*$" command)
  (let ((proc (start-process "kid-shell" nil shell-file-name
  shell-command-switch
  (substring command 0 (match-beginning 0)))))
  (set-process-sentinel proc 'shell-command-sentinel))
  ad-do-it))
(ad-activate 'dired-run-shell-command)

这个缺点就是所有以 & 结尾的程序的输出都直接被丢弃了,但是我觉得几乎这 样的程序输出都是不需要的,而且常规的文件管理器也通常都直接丢弃这些输出 吧。如果实在是不愿意,可以另外定义一种语法(比如末尾是 &! 的才丢弃输出) 并修改那个函数里面相应的匹配的正则表达式就可以了。

另外,其实有许多不同的类型的文件使用相同的命令打开,可以把他们归到一组 (例如,各种 video 文件都用 mplayer 打开),但是 dired-x.el 并不提供这个 功能,所以我写了一个宏来从“表面”上实现这个功能:
(defmacro kid-dired-define-assoc-group (patterns actions &optional name)
  "define an assoc entry to help dired guess the shell command.
PATTERN is a list of regexps used to match the filename.
NAME is a list of string or expression which eval to a string
to denote what shell command to execute. Optional NAME is
the name of this group , just for documentation purpose."
  (let ((item (gensym)))
  `(dolist (,item ',patterns)
  (setq dired-guess-shell-alist-user
  (cons (list ,item ,@actions) dired-guess-shell-alist-user)))))

(add-hook 'dired-load-hook
  (lambda ()
  (setq dired-guess-shell-alist-user nil)
  (kid-dired-define-assoc-group
  ("rm" "rmvb" "RM" "RMVB" "avi" "mpg" "mpeg")
  ("mplayer * &")
  video)
  (kid-dired-define-assoc-group
  ("pdf" "PDF")
  ("acroread * &" "xpdf * &")
  pdf-document)
  (kid-dired-define-assoc-group
  ("png" "jpg" "jpeg" "gif")
  ("xloadimage * &" "gqview * &")
  image)
  (kid-dired-define-assoc-group
  ("chm" "CHM")
  ("xchm * &")
  chm-document)
  (kid-dired-define-assoc-group
  ("html" "HTML" "htm" "HTML")
  ("firefox * &"))))
使用 run-assoc.el

事实上这个功能非常简单,同样是定义一个关联列表,比 dired-x.el 要简单, 而且也不够灵活。EmacsWiki 上有相关介绍。
使用 trivial-mode.el

trivial-mode.el 允许你定义一个 "trivial mode" ,并把他添加到 auto-mode-alist 里面去,比如,定义了一个所有 pdf 文件用 xpdf 打开的 trivial mode ,那么当在 Emacs 里面打开 pdf 的时候会自动调用 xpdf 打开 这个文件。当然这个扩展可以完全独立于 dired 而使用,还是比较方便的。

排序

方便的排序功能自然是一个好的文件管理器所比不可少的了。
使用 dired-sort-menu.el 和 dired-sort-menu+.el

dired-sort-menu.el 提供了强大的排序功能,可以按照名称、修改时间、访问时 间、大小、扩展名等进行排序,还可以逆序排序和递归排序。同时,它还提供了 一个菜单以及一个对话框(使用 Emacs 的 widget 库来实现的,就是说,在终端 下面也是可以用的)来选择排序功能。可以使用 C-d 来打开这个对话框,或者用 Shift+mouse-2 来打开弹出菜单选择排序项目。 dired-sort-menu+.el 对 dired-sort-menu.el 做了一定的修改。
自己对 dired 进行扩展

我们可以通过 dired-sort-other 给 ls 程序传递参数来达到排序的目的, gnu 的 ls 程序有很多选项可用: [tSXUucrR] 。

我们这里使用 dired 提供的 dired-sort-other 来调用 gnu ls 来进行排序, 为了方便记忆,我覆盖掉 dired 的 s 键(原本用于切换按照时间还是文件名进 行排序),作为一个前缀,然后后面的键着直接对应于 ls 的相关参数,例如 s X 则是对扩展名进行排序。另外,还定义了两个开关, s r 用于打开或关闭逆 序排序,而 s R 用于打开或关闭递归排序。
(defmacro kid-dired-define-sort (key switch)
  "define the key `s KEY' to sort use switch to ls."
  `(define-key dired-mode-map ,(concat "s" key)
  (lambda ()
  (interactive)
  (dired-sort-other (concat dired-listing-switches
  ,switch
  (if kid-dired-sort-reverse
  "r"
  "")
  (if kid-dired-sort-recursive
  "R"
  ""))))))
(defmacro kid-dired-define-toggle (key var)
  `(define-key dired-mode-map ,(concat "s" key)
  (lambda ()
  (interactive)
  (setq ,var (not ,var))
  (message "%s %s."
  (get ',var 'variable-documentation)
  (if ,var
  "enabled"
  "disabled")))))
(add-hook 'dired-mode-hook
  (lambda ()
  ;; dired use `s' to switch sort by name/time, we undefine it so
  ;; that it can be used as prefix
  (define-key dired-mode-map
  (kbd "s")
  nil)
  (defvar kid-dired-sort-reverse nil
  "sort reversely")
  (defvar kid-dired-sort-recursive nil
  "sort recursively")
  (kid-dired-define-sort "X" "X")
  (kid-dired-define-sort "t" "t")
  (kid-dired-define-sort "S" "S")
  (kid-dired-define-sort "U" "U")
  (kid-dired-define-sort "u" "ut") ; sort by access time
  (kid-dired-define-sort "c" "ct") ; sort by ctime
  (kid-dired-define-sort "n" "") ; sort by name :)
  (kid-dired-define-toggle "r" kid-dired-sort-reverse)
  (kid-dired-define-toggle "R" kid-dired-sort-recursive)))
使用 dired 自身的功能

我是在作了那个扩展之后才知道 dired 自己就有这个功能的,都怪自己不仔细 看文档,导致了重复劳动! C-u s 就可以编辑 dired 的 dired-listing-switches 这个变量,从而达到控制排序的方法的目的。


没有评论:

博客归档