I want to view ~/.zsh_history files the way they are displayed by the history command with raw unixtimestamps converted to properly formatted dates.

: 1568128379:0;cp -a ~/.zshrc.pre-oh-my-zsh ~/.zshrc
: 1568128381:0;exit

So a command like readhist.sh.el < ~/.zsh_history on the lines above would produce output like

1 2023-10-21 cp -a ~/.zshrc.pre-oh-my-zsh ~/.zshrc
2 2023-10-21 exit

The first part was to get it to work in the editor which I solved in the link - https://emacs.stackexchange.com/a/79254/15477.

In the editor

the search string is: \(: \)\([0-9]\{10\}\)\(:0;\)

the replacement string: \,(concat (format "%6d " 9999) (rgx-get-time-string (match-string 2)) " "))

with rgx-get-time-string being a function to convert the unixtimestamp correctly.

(defun rgx-get-time-string (unixtimestr)
  (format-time-string "%Y-%m-%d %H:%M" (string-to-number unixtimestr)))

My attempt to convert to a script results the replacement inserted literally rather than computed.

The 9999 in the concat command is a place holder I will replace with an appropriate expression later.later.

!/usr/local/bin/emacs --script
;;-*- mode: emacs-lisp;-*-
    (defun process (histline)
      "return the usual string"
      (replace-regexp-in-string "\\(: \\)\\([0-9]\\{10\\}\\)\\(:0;\\)"
                                "\,(concat (format \"%6d \"
      (rgx-get-time-string (match-string 2)) \" \"))" histline))
    (defun rgx-get-time-string (unixtimestr)
      (format-time-string "%Y-%m-%d %H:%M" (string-to-number unixtimestr)))
    (condition-case nil
        (let (line)
           (while (setq line (read-from-minibuffer ""))
            ;;(princ line)
            (princ (process line))
            (princ "\n")))
      (error nil))
  • "scripting the solution" - The solution to what question/problem? and "it doesn't" [work] - What doesn't work? What does doesn't work mean here? The question isn't clear.
    – Drew
    Commented Oct 25, 2023 at 19:12
  • The question needs to stand on its own. If something important needs to be added from some other question/answer, then include that here.
    – Drew
    Commented Oct 25, 2023 at 19:14
  • @Drew The first problem was I had to double the slashes in the regular expression. Now the problem is the \, syntax used in replace-regexp when the replacement is a lisp expression doesn't seem to work with replace-regexp-in-string
    – vfclists
    Commented Oct 25, 2023 at 19:22
  • 1
    The \, syntax in replace-regexp only works when you call it interactively. There is no such facility for replace-regexp-in-string (it's not an interactive function, i.e. a command) but you can use a function, instead of a string, as the replacement. C-h f replace-regexp-in-string is, as always, your friend.
    – NickD
    Commented Oct 25, 2023 at 19:30
  • 1
    You really need to edit it to specify exactly what your question is. Comments could end up getting deleted, in which case the question would be unclear.
    – db48x
    Commented Oct 25, 2023 at 22:51

1 Answer 1


When in doubt, ask Emacs. Use C-h f to look up the help for the function replace-regexp-in-string. You’ll get something like this:

replace-regexp-in-string is a byte-compiled function defined in subr.el.gz.

(replace-regexp-in-string REGEXP REP STRING &optional FIXEDCASE LITERAL SUBEXP START)

Replace all matches for REGEXP with REP in STRING.

Return a new string containing the replacements.

Optional arguments FIXEDCASE, LITERAL and SUBEXP [blah blah blah…].

REP is either a string used as the NEWTEXT arg of replace-match or a
function.  If it is a function, it is called with the actual text of each
match, and its value is used as the replacement text.  When REP is called,
the match data are the result of matching REGEXP against a substring
of STRING, the same substring that is the actual text of the match which
is passed to REP as its argument.


See the bit at the end about the REP argument? It can be a function, like this:

(replace-regexp-in-string "\\(: \\)\\([0-9]\\{10\\}\\)\\(:0;\\)"
                          (lambda (match)
                            (concat (format "%6d " 9999)
                                    (rgx-get-time-string match)
                                    " "))

Incidentally, if that 9999 is supposed to become a line number, then you could just pipe the output through cat -n instead. That will save you some work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.