Here are the ingredients to keeping a timestamped history:
- Emacs shell buffer: M-x shell.
- Timestamp script running in the background.
- Lisp code to remove shell output, leaving prompts and history.
su
, ssh
, and even things like ftp. If you need multiple shell buffers (say, on different hosts), use M-x rename-buffer.The hitch is that after a few weeks in your emacs session, a shell buffer can grow very large. Here is a Lisp function to put in your .emacs which knocks it back down to size (make sure the emacs shell-prompt-pattern variable matches your shell prompt):
(defun engisneering-clean-shell-buffer ()
(interactive)
(let ((beg (point-min)))
(goto-char (point-min))
(if (> 10000 (buffer-size)) (buffer-disable-undo))
(while (re-search-forward shell-prompt-pattern nil t)
(if (not (= (point) (line-end-position)))
(progn
(forward-line 0) ;; beginning of line, ignore prompt boundary
(if (not (= (point) beg))
(delete-region beg (point)))
(forward-line 1)
(setq beg (point)))))
(buffer-enable-undo)
)
)
That will also be useful when you're ready to save your timestamped history. The timestamping is easy -- put this shell script into a file called "timestamp", and give it execute permission:
#!/bin/bash
while ((1)); do
echo -n "timestamp> "
date
echo -n "prompt> "
sleep 3600
done
There are two things to note about the script. First, when it outputs a timestamp, it will match the prompt pattern, so that it won't get deleted by our Lisp cleaning function. Second, it ends with "prompt> " so that the next time you hit return, emacs doesn't think the output from the timestamp is part of your next command.
So, whenever you start a shell buffer, run "timestamp&", to get the date and time printed out once an hour while you do other things.
Finally, here is a little script to help you save your shell histories. Create a directory to hold your histories; name the history files after the host the shell was on, and date them (including the year). Save them periodically, just like any of your work.
(defun engisneering-save-shell-buffer ()
(interactive)
(let ((name (buffer-name)))
(save-buffer 0)
(setq buffer-file-name nil)
(rename-buffer name)
(buffer-disable-undo)
(delete-region (point-min) (point-max))
(buffer-enable-undo)
)
)