fish shell (and zsh) let you register commands that run before and after every command.

I have my shell configured to print the exit status if nonzero and the amount of time taken to execute the command in seconds if longer than 10 seconds.

Here's an implementation of that behavior in fish

# ~/.config/fish/config.fish

# set a global variable to the current epoch time in seconds
# before executing any comment
function get_start --on-event fish_preexec
    set -g cmdstart (/bin/date '+%s')

# inspect previous exit status, write "Exit n"
# where n is a non-zero exit status
function show_exit_status
    switch $status
    case 0
    case '*'
        echo "Exit $status"

# compute the time in seconds since the command was started.
# print it if the time exceeds 10 seconds
function show_time_consumed
    set taken (math (/bin/date '+%s') - $cmdstart)
    if math $taken '>=' 10 >/dev/null
        printf 'Time: %ss\n' $taken

# show the time consumed and the exit status
# after the command is run
function postcmd --on-event fish_postexec

And an example of what it does

> sleep 14
Time: 14s

sleep 14; /bin/false
Exit 1
Time: 14s

I'm trying to figure out how to emulate the same behavior in eshell.

Here's my initial attempt to port it:

(setq inhibit-startup-echo-area-message "USERNAME")
(setq inhibit-startup-message t)

(defvar eshell-current-command-start-time (float-time))

(defun eshell-current-command-start ()
  "store the current starting time"
  (setf eshell-current-command-start-time (float-time)))

(defun eshell-current-command-stop ()
  "write the stopping time"
  (let ((the-time (float-time)))
    (message (number-to-string
           (- the-time eshell-current-command-start-time)))))

(add-hook 'eshell-pre-command-hook #'eshell-current-command-start)

(add-hook 'eshell-post-command-hook #'eshell-current-command-stop)

It has some problems that hamper its usefulness.

  • When you hit enter after an empty command, the post-command-hook runs, but the pre-command-hook does not.
  • Neither message nor print write to the eshell buffer.

Is there a straightforward way of grabbing the time using float-time before and after every command executes (including empty commands) and displaying it in the buffer the same way that the output of a command would be displayed?


Here is my attempt

(defvar-local eshell-current-command-start-time nil)

(defun eshell-current-command-start ()
  (setq eshell-current-command-start-time (current-time)))

(defun eshell-current-command-stop ()
  (when eshell-current-command-start-time
     (format "Time: %.0fs\n"
              (time-subtract (current-time)
    (setq eshell-current-command-start-time nil)))

(defun eshell-current-command-time-track ()
  (add-hook 'eshell-pre-command-hook #'eshell-current-command-start nil t)
  (add-hook 'eshell-post-command-hook #'eshell-current-command-stop nil t))

(add-hook 'eshell-mode-hook #'eshell-current-command-time-track)
;; To uninstall
;; (remove-hook 'eshell-mode-hook #'eshell-current-command-time-track)

Test it with some simple commands in Eshell

Welcome to the Emacs shell

~ $ sleep 3
Time: 3s
~ $ date && sleep 1
Sat Jul 14 20:52:18 2018
Time: 1s
~ $
  • adding (when eshell-current-command ...) to eshell-current-comand-stop seems to do the trick for suppressing time information when the command is empty. Jul 14 '18 at 21:17
  • @GregoryNisbet eshell-current-command-stop also needs to reset eshell-current-command to nil, I have updated the code.
    – xuchunyang
    Jul 15 '18 at 1:35
  • The question also mentions the exit code of the last command. Any idea how to get that? (emacs.stackexchange.com/q/66879/10090) Aug 3 at 9:08
  • @ZelphirKaltstahl eshell-last-command-status, I believe it works for external command such as /bin/grep, but doesn't work for Emacs Lisp commands such as grep/cd/dired.
    – xuchunyang
    Aug 4 at 6:57
  • @xuchunyang Thanks for that! For me it does Symbol’s function definition is void: eshell-last-command-status. I don't want to escalate these comments into a conversation. If you post it as an answer, I could comment on that and perhaps check as correct answer. Aug 4 at 13:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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