Elisp: Batch Find Replace

By Xah Lee. Date: . Last updated: .

This page shows emacs lisp scripts that do variations of find/replace string. For example, i need a script that reports the position of a given string for 5 thousand files. Another example: replace all HTML page's <h1> tag text from its <title> tag text.

Problem: Report String Position

I need to know if a particular string happens in beginning of file or near the end. Ι need to know this for about 5k files in a directory.

Solution

;; -*- coding: utf-8 -*-
;; 2011-03-21
;; report the line number of a occurrences of string, of a given dir

(setq inputDir "~/web/ergoemacs_org/emacs/" )

;; add a ending slash if not there
(when (not (string-equal "/" (substring inputDir -1)))
  (setq inputDir (concat inputDir "/")))

(defun my-process-file (fPath)
  "process the file at fullpath fPath …"
  (let (myBuffer (ii 0) searchStr)

    (when (not (string-match "/xx" fPath)) ; skip dir starting with xx

      (setq myBuffer (get-buffer-create " myTemp"))
      (set-buffer myBuffer)
      (insert-file-contents fPath nil nil nil t)

      (setq case-fold-search nil) ; NOTE: remember to set case sensitivity here

      (setq searchStr "<style>" )

      (goto-char (point-min))
      (while (search-forward searchStr nil t) ; NOTE: for regex, use re-search-forward
        (princ (format "this many: %d %s\n" (line-number-at-pos (point)) fPath)))

      (kill-buffer myBuffer))))

(require 'find-lisp)

(let (outputBuffer)
  (setq outputBuffer "*xah occur output*" )
  (with-output-to-temp-buffer outputBuffer
    (mapc 'my-process-file (find-lisp-find-files inputDir "\\.html$"))
    (princ "Done deal!")))

Modify the “inputDir” and “searchStr” above and test it on your own machine.

For explanation of this code, see: Elisp: Write grep.

Problem 2: Fix HTML “TITLE” and “H1” Tags

Today, while i working on my website, i noticed some HTML files are missing a “H1” header tag. While in another directory, i wish to replace all “TITLE” tag content by the one from “H1” tag.

So, i need a script that fix these tag's texts.

Solution

Here's a function that gets a file's “title” tag text.

(defun xah-html-get-html-file-title (fname)
  "Return FNAME <title> tag's text.
Assumes that the file contains the string
“<title>…</title>”."
  (with-temp-buffer
    (insert-file-contents fname nil nil nil t)
    (goto-char (point-min))
    (buffer-substring-no-properties
     (search-forward "<title>") (- (search-forward "</title>") 8))))

I also need to get the “H1” tag text. So i just quickly did a copy-paste coding:

(defun xah-html-get-html-file-h1 (fname)
  "Return fname <h1> tag's text.
Assumes that the file contains the string
“<h1>…</h1>”."
  (with-temp-buffer
    (insert-file-contents fname nil nil nil t)
    (goto-char (point-min))
    (buffer-substring-no-properties
     (search-forward "<h1>") (- (search-forward "</h1>") 5))))

It's not efficient to open file twice to get “title” and “h1” texts, but that's ok, because my whole script will finish running in a few seconds anyway and this is just one-time use.

Now, here's the code i wrote quickly to fix the tags:

;; -*- coding: utf-8 -*-
;; 2011-03-20
;; change title to h1 tag's text in “Time Machine” pages
;; 
;; for each HTML page in [~/web/xahlee_org/p/time_machine/]
;; if the title tag and h1 tag text differ, make the title use h1's text

(setq inputDir "~/web/xahlee_org/p/time_machine/" ) ; dir must end with a slash

(defun my-process-file (fPath)
  "process the file at fullpath fPath …"
  (let ( titleText h1Text p1 p2)

    (setq h1Text (get-html-file-h1-text fPath))
    (setq titleText (get-html-file-title fPath))

    (if (equal h1Text titleText)
        nil
      (progn
        (find-file fPath )
        (goto-char (point-min))
        (search-forward "<title>" )
        (setq p1 (point) )

        (search-forward "</title>" )
        (backward-char 8)
        (setq p2 (point) )

        (delete-region p1 p2 )
        (insert h1Text)
        (print fPath)
        ))
    ))

(require 'find-lisp)

(let (outputBuffer)
  (setq outputBuffer "*process time machine output*" )
  (with-output-to-temp-buffer outputBuffer
    (mapc 'my-process-file (find-lisp-find-files inputDir "\\.html$"))
    (princ "Done deal!")
    )
  )

Again, all the above script are variations of find replace. For code detail, see: Elisp: Write grep and Elisp: Find String Inside HTML Tag.

In this script, i didn't include code to save the changed file. This way, i can do some manual verification after the script has run. When i want them all saved, i just call ibuffer and type 3 keys * u S to have all of them saved, and D y closes them all. [see Emacs: List Buffers]

If you have a question, put $5 at patreon and message me on xah discord.
Or support me by Buy Xah Emacs Tutorial

Emacs Tutorial

Emacs Init

Emacs Keys

ELisp

ELisp Examples

ELisp Write Major Mode


ELisp Examples

Xah Commands

Text Transform Under Cursor

Commands Do thing-at-point

Command to Insert Things

Script Examples

Misc