Dired (Directory Editor) is what happens when you realize that file management is just text editing in disguise. Why click and drag when you can mark, filter, and batch-rename with regular expressions? Why open a separate file manager when Emacs IS the file manager?
Dired isn’t just a file browser—it’s a file editor. And once you understand that distinction, you’ll never want to use Finder or Explorer again.
C-x d
Or just C-x C-f on a directory instead of a file. Boom, you’re in Dired.
What you see is a buffer. A regular, editable buffer. That lists files. And you can edit it. This is going to get wild.
Standard movement keys work, but these are Dired-specific:
RET or f - Open file/directoryo - Open in other window^ - Go up to parent directoryn / p - Next/previous line< / > - Beginning/end of bufferj - Jump to file (with completion)🚸 IDE Refugee Note: This isn’t a tree view like VS Code’s sidebar. It’s a full buffer showing one directory. But trust me, this is more powerful.
Marking is how you select multiple files for operations:
m - Mark fileu - Unmark fileU - Unmark allt - Toggle marks* * - Mark executables* / - Mark directories* s - Mark all files in subdirs% m - Mark by regexpExample: Mark all Python files:
% m \.py$ RET
Once marked, unleash the power:
C - Copy marked filesR - Rename/move marked filesD - Delete marked filesS - Create symlinksH - Create hardlinksM - Change mode (chmod)O - Change owner (chown)G - Change group (chgrp)Z - Compress/uncompressThe workflow:
mC for copy)This is where minds get blown. Press:
C-x C-q
Now the buffer is editable. You can:
When done, C-c C-c to save changes or C-c C-k to cancel.
🤔 Why Though? Because renaming files IS text editing. Why should it be different? In Dired, it isn’t.
Want to rename 100 files? Easy:
C-x C-qM-%! to replace allC-c C-c to executeExample: Add date prefix to all files:
M-% ^ RET 2024-01-20_ RET !
Add to your config:
(require 'dired-x)
(add-hook 'dired-mode-hook 'dired-omit-mode) ; Enable omit mode
(setq dired-omit-files "^\\.?#\\|^\\.$\\|^\\.\\.$\\|^\\..*$")
New powers:
C-x C-j - Jump to dired buffer of current file* . - Mark extension* O - Mark omitted filesF - Visit all marked files;; Auto-revert dired buffers
(setq global-auto-revert-non-file-buffers t)
(setq auto-revert-verbose nil)
(add-hook 'dired-mode-hook 'auto-revert-mode)
;; Preview files
(use-package peep-dired
:ensure t
:bind (:map dired-mode-map
("P" . peep-dired)))
;; Or use dired-preview
(use-package dired-preview
:ensure t
:config
(dired-preview-global-mode 1))
(use-package dired-subtree
:ensure t
:bind (:map dired-mode-map
("TAB" . dired-subtree-toggle)
("<tab>" . dired-subtree-toggle)))
Now TAB expands directories inline. It’s like a tree view, but better.
Mark files by content:
(defun dired-mark-files-containing-regexp (regexp &optional marker-char)
"Mark files containing REGEXP."
(interactive
(list (read-string "Mark files containing (regexp): ")
(if current-prefix-arg ?\040)))
(let ((dired-marker-char (or marker-char dired-marker-char)))
(dired-mark-if
(and (not (looking-at dired-re-dot))
(not (looking-at dired-re-dir))
(let ((filename (dired-get-filename nil t)))
(when filename
(with-temp-buffer
(insert-file-contents filename)
(re-search-forward regexp nil t)))))
"files containing regexp")))
(use-package image-dired
:ensure nil ; Built-in
:bind (:map dired-mode-map
("C-t i" . image-dired)))
;; Better: dired-rainbow for color-coded files
(use-package dired-rainbow
:ensure t
:config
(dired-rainbow-define-chmod directory "#6cb2eb" "d.*")
(dired-rainbow-define html "#eb5286" ("css" "less" "sass" "scss" "htm" "html"))
(dired-rainbow-define xml "#f2d024" ("xml" "xsd" "xsl" "xslt" "wsdl")))
(use-package dired-git-info
:ensure t
:bind (:map dired-mode-map
(")" . dired-git-info-mode)))
;; Show git status in dired
(use-package diff-hl
:ensure t
:hook (dired-mode . diff-hl-dired-mode))
+ - Create directoryC-x C-f - Create new file (just type a new name);; Find files by name
(use-package find-dired
:ensure nil
:bind (:map dired-mode-map
("C-c C-f" . find-name-dired)))
;; fd integration (faster find)
(use-package fd-dired
:ensure t)
(use-package async
:ensure t
:config
(dired-async-mode 1))
Now large copy/move operations won’t freeze Emacs.
;; My complete dired setup
(use-package dired
:ensure nil
:custom
(dired-listing-switches "-alhF --group-directories-first")
(dired-omit-files "^\\.[^.].*")
(dired-omit-verbose nil)
(dired-dwim-target t) ; Suggest other dired buffer as target
(dired-kill-when-opening-new-dired-buffer t)
:config
(setq dired-recursive-copies 'always)
(setq dired-recursive-deletes 'always)
(setq delete-by-moving-to-trash t)
;; Auto refresh
(setq global-auto-revert-non-file-buffers t)
;; Human readable sizes and better formatting
(when (executable-find "ls")
(setq dired-listing-switches "-alhF --group-directories-first")))
;; Open file externally
(defun dired-open-file ()
"Open file with default application."
(interactive)
(let ((file (dired-get-filename nil t)))
(cond
((eq system-type 'darwin) (call-process "open" nil 0 nil file))
((eq system-type 'gnu/linux) (call-process "xdg-open" nil 0 nil file))
(t (message "Unsupported system")))))
(define-key dired-mode-map (kbd "E") 'dired-open-file)
;; Quick backup
(defun dired-backup-file ()
"Create backup of file at point."
(interactive)
(let ((file (dired-get-filename))
(backup-name (concat (dired-get-filename) ".bak")))
(copy-file file backup-name 1)
(revert-buffer)
(message "Created backup: %s" backup-name)))
;; Archive marked files
(defun dired-archive-marked-files ()
"Archive marked files to tar.gz."
(interactive)
(let* ((files (dired-get-marked-files))
(archive (read-string "Archive name: ")))
(dired-do-shell-command
(format "tar czf %s.tar.gz" archive)
nil files)))
;; Quick directory bookmarks
(defun dired-bookmark-jump ()
"Jump to bookmarked directories."
(interactive)
(let ((dir (completing-read "Directory: "
'("~/"
"~/Documents/"
"~/Downloads/"
"~/projects/"
"~/.emacs.d/"))))
(dired dir)))
(global-set-key (kbd "C-c j") 'dired-bookmark-jump)
(defun dired-dual-panel ()
"Open dired in dual panel mode."
(interactive)
(delete-other-windows)
(dired "~/")
(split-window-horizontally)
(other-window 1)
(dired "~/"))
;; Now marking and copying between panels is easy!
Open remote directories:
C-x d /ssh:user@host:/path/
All dired operations work on remote files!
;; Icons in dired
(use-package all-the-icons-dired
:ensure t
:hook (dired-mode . all-the-icons-dired-mode))
;; Narrow dired to match filter
(use-package dired-narrow
:ensure t
:bind (:map dired-mode-map
("/" . dired-narrow)))
;; Ranger-like preview
(use-package ranger
:ensure t
:config
(setq ranger-cleanup-on-disable t))
(defun dired-organize-photos ()
"Organize photos by date."
(interactive)
(dired-mark-files-regexp "\\.\\(jpg\\|jpeg\\|png\\|gif\\)$")
(let ((photos (dired-get-marked-files)))
(dolist (photo photos)
(let* ((attributes (file-attributes photo))
(mod-time (nth 5 attributes))
(date-dir (format-time-string "%Y-%m" mod-time)))
(unless (file-exists-p date-dir)
(make-directory date-dir))
(rename-file photo (expand-file-name (file-name-nondirectory photo) date-dir))))))
(defun dired-rename-with-counter ()
"Rename marked files with counter."
(interactive)
(let ((prefix (read-string "Prefix: "))
(counter 1))
(dolist (file (dired-get-marked-files))
(let* ((extension (file-name-extension file))
(new-name (format "%s_%03d.%s" prefix counter extension)))
(rename-file file new-name)
(setq counter (1+ counter))))
(revert-buffer)))
Chapter 11 covers macros and registers—the automation tools that make repetitive tasks disappear. But now that you know Dired, you might never need another file manager again.
Dired exemplifies the Emacs philosophy: everything is text, everything is editable, everything is programmable. Your file manager isn’t a separate application—it’s just another buffer in Emacs. And that changes everything.
“I used to have a file manager. Now I have Dired. The file manager is gathering dust somewhere, probably in ~/.trash where Dired put it.” —Reformed File Manager User