3

I'm trying to create a keyboard macro to generate a report which displays the number of times a certain string occurs. I have two buffers,

  1. with all the strings to look for(on a separate line)
  2. the target buffer containing all the text to be scanned/counted.

I would like to append the number of occurences after each line. With M-x count-matches I see the number in my info bar, but I would like it in my kill ring or register so I can append it to the line in the first buffer.

Any other takes on how to solve this more efficiently are welcome also (elisp/sed/awk?)

Drew
  • 75,699
  • 9
  • 109
  • 225
dr jerry
  • 321
  • 1
  • 7
  • because every string is on separate line in the target buffer, I go for a `grep -c` still curious to see the answer. – dr jerry Feb 01 '22 at 15:45
  • You can use Elisp: `count-matches` *returns* the number of matches. And unless you call it interactively it doesn't print a message showing that number. – Drew Feb 01 '22 at 16:23

2 Answers2

4

Assume you have two buffers: one with a list of strings to look for, one string per line, the other the buffer to search for the strings.

Invoke this command in the buffer that lists the strings to search for. (You can bind it to a key or invoke it with M-x.)

(defun show-counts (search-buffer)
  "Add SEARCH-BUFFER counts for strings listed in current buffer."
  (interactive "bSearch buffer: ")
  (let (string)
    (goto-char (point-min))
    (setq string  (regexp-quote
                   (buffer-substring (line-beginning-position)
                                     (line-end-position))))
    (while (not (eobp))
      (goto-char (line-end-position))
      (insert (format "\t%s" (with-current-buffer search-buffer
                               (count-matches
                                string (point-min) (point-max)))))
      (forward-line)
      (setq string  (regexp-quote
                     (buffer-substring (line-beginning-position)
                                       (line-end-position)))))))

If you want to allow regexps instead of literal strings, remove the regexp-quote wrappers.

Drew
  • 75,699
  • 9
  • 109
  • 225
1

If you really just wanted to use an "Emacs Keyboard Macro" you could cheat it via copying the output from the "count-matches" within the *Messages* buffer:

i.e.

Macro:

                     ;;                         (Start in the "search word" buffer)
C-SPC                ;; set-mark-command        (Copy the search word to the kill ring)
C-e                  ;; move-end-of-line
ESC W                ;; kill-ring-save 
C-x o                ;; other-window            (Switch to the "text" buffer)
ESC <                ;; beginning-of-buffer
<<count-matches>>    ;; count-matches      
C-y                  ;; yank                    (Count the matches to the saved search word)
RET                  ;; newline
C-x b                ;; switch-to-buffer        (Switch to the *Messages* buffer)
*Messages*           ;; self-insert-command * 10
RET                  ;; newline 
ESC >                ;; end-of-buffer
C-r                  ;; isearch-backward        (Search backwards for the output from "count-matches")
occurence            ;; self-insert-command * 10
RET                  ;; newline
C-a                  ;; move-beginning-of-line  (Copy the number from the "X occurrence" string)
C-SPC                ;; set-mark-command
M-f                  ;; forward-word
ESC w                ;; kill-ring-save
C-x b                ;; switch-to-buffer        (Restore the "text" buffer)
RET                  ;; newline
C-x o                ;; other-window            (Switch to the "search word" buffer)
SPC                  ;; self-insert-command
C-y                  ;; yank                    (Append the number to the end of the search line)
C-n                  ;; next-line               (Move to the next search term)
C-a                  ;; move-beginning-of-line
Brian
  • 11
  • 1