Emacs Lisp Idioms for Text Processing in Batch Style

Buy Xah Emacs Tutorial. Master emacs benefits for life.
, , …,

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 with-temp-buffer.

(defun my-process-file (fPath)
  "Process the file at path FPATH …"
  (with-temp-buffer fPath
    (insert-file-contents fPath)
    ;; process it …
    ) )

Modifying Files

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 )
      (insert-file-contents fPath)

      ;; process text …
      ;; set fileChanged-p to true/false

      (when fileChanged-p (write-region 1 (point-max) fPath) ) ) ) )

If you always need to change every file, use with-temp-file.

Note: you should not use find-file or 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

Filename 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)


(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."
  (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 shell-command or shell-command-to-string.

; 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 or start-process-shell-command. Here a example:

;; open files in Linux desktop
 (lambda (fPath)
   (let ((process-connection-type nil))
     (start-process "" nil "xdg-open" fPath)) )

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. The 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.

Processing Lines

Emacs Lisp: Functions for Processing Lines

Print, Princ, Prin1, Format, Message

Emacs Lisp: print, princ, prin1, format, message

Practical Examples

For some practical examples of batch style text processing, see:

( Thanks to Rubén Berenguel for a correction.) ( thx to Phil Hudson for a tip.)

Like it?
Buy Xah Emacs Tutorial
or share
blog comments powered by Disqus