Emacs Lisp Idioms for Text Processing in Batch Style
This page shows common programing patterns of emacs lisp for batch text processing. Typically the type of tasks one would do in unix shell tools or Perl. For example, find/replace on a list of given files or dir, process (small sized) log files, compile a bunch of files, generating a report.
If you don't know elisp, see: Emacs Lisp Basics.
Running Emacs Lisp Script in Shell
Emacs Lisp: Run Elisp Script in Shell
Reading ＆ Writing to File
Read-Only Text Processing
To process thousands of files, read only, use
(defun my-process-file (fPath) "Process the file at path FPATH …" (with-temp-buffer (insert-file-contents fPath) ;; process it … ))
If you want to write to file ONLY when you actually changed the file, you can create flag variable and call
write-region, like this:
(defun my-process-file (fPath) "Process the file at path FPATH …" (let ((fileChanged-p nil)) (with-temp-buffer (insert-file-contents fPath) ;; process text ;; set fileChanged-p to t or nil (when fileChanged-p (write-region 1 (point-max) fPath)))))
If you always need to change every file, use
Note: you should not use
write-file, because they have many side-effects and is slow.
See: Emacs Lisp Text Processing: find-file vs with-temp-buffer.
Read File Content as String or List of Lines
Emacs Lisp: Read File Content as String or List of Lines
File ＆ Dir Manipulation
Commonly used functions to manipulate file names.
(file-name-directory f) ; get dir path (file-name-nondirectory f) ; get file name (file-name-extension f) ; get suffix (file-name-sans-extension f) ; remove suffix (file-relative-name f ) ; get relative path (expand-file-name f ) ; get full path default-directory ; get the current dir (this is a variable)
(info "(elisp) File Names")
File ＆ Dir Manipulation
Commonly used functions to manipulate files ＆ dirs.
(file-exists-p FILENAME) (rename-file FILE NEWNAME &optional OK-IF-ALREADY-EXISTS) (copy-file FILE NEWNAME &optional OK-IF-ALREADY-EXISTS KEEP-TIME PRESERVE-UID-GID) (delete-file FILE) (set-file-modes FILE MODE)
;; get list of file names (directory-files DIR &optional FULL MATCH NOSORT) ;; create a dir. Non existent paren dirs will be created (make-directory DIR &optional PARENTS) ;; copy/delete whole dir (delete-directory DIRECTORY &optional RECURSIVE) ; RECURSIVE option new in emacs 23.2 (copy-directory DIR NEWNAME &optional KEEP-TIME PARENTS) ; new in emacs 23.2
(info "(elisp) Files")
Example: make backup file.
(defun make-backup () "Make a backup copy of current buffer's file. Create a backup of current buffer's file. The new file name is the old file name with trailing “~”, in the same dir. If such a file already exist, append more “~”. If the current buffer is not associated with a file, its a error." (interactive) (let (fName backupName) (setq fName (buffer-file-name)) (setq backupName (concat fName "~")) (while (file-exists-p backupName) (setq backupName (concat backupName "~"))) (copy-file fName backupName t) (message (concat "Backup saved as: " (file-name-nondirectory backupName)))))
Find the Current Elisp Script's File Name Programmatically
Emacs Lisp: Get Script Name at Run Time, Call by Relative Path
Calling a Shell Command
Call a shell command, wait for it to finish before continuing, use
; idiom for calling a shell command (shell-command "cp /somepath/myfile.txt /somepath") ; idiom for calling a shell command and get its output (shell-command-to-string "ls")
Call a shell command, but don't wait for it to finish before continuing, use
start-process-shell-command. Here a example:
;; open files in Linux desktop (mapc (lambda (fPath) (let ((process-connection-type nil)) (start-process "" nil "xdg-open" fPath)) ) myFileList)
For detail, see: Emacs Dired: Opening Files in External Apps.
(info "(elisp) Asynchronous Processes")
Traverse a directory
In the following, “my-process-file” is a function that takes a file full path as input.
The “find-lisp-find-files” will generate a list of full paths, using a regex on file name.
mapc will apply the function to elements in a list.
; idiom for traversing a directory (require 'find-lisp) (mapc 'my-process-file (find-lisp-find-files "~/web/emacs/" "\\.html$"))
Getting Command Line Arguments
To get arguments passed from the command line, use the built-in variable argv. See: Getting Command Line Arguments.
Emacs Lisp: Functions for Processing Lines
Print, Princ, Prin1, Format, Message
Emacs Lisp: print, princ, prin1, format, message
For some practical examples of batch style text processing, see:
- How to Write grep in Emacs Lisp
- Generate a Web Links Report with Emacs Lisp
- Creating a Sitemap with Emacs Lisp
- Processing HTML: Transform Tags to HTML5 “figure” and “figcaption” Tags
- Validate Matching Brackets
( Thanks to Rubén Berenguel for a correction.) ( thx to Phil Hudson for a tip.)