3

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')
end

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

# 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
    end
end

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

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?

4

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
    (eshell-interactive-print
     (format "Time: %.0fs\n"
             (float-time
              (time-subtract (current-time)
                             eshell-current-command-start-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. – Gregory Nisbet 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

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.