Ubuntu – Last command and execution time

command linenotification

I'd like to get the last command used and its execution time. This will be used as part of an alias for notification.

Best Answer

NOTE: I'm assuming bash as the shell.

Adapting to other shells shouldn't be difficult.

Getting the last command used is trivial: it's stored in the shell event designator !!, and can also be obtained using the shell builtins fc (fc -s) or history (with some string manuipluation: history 1 | awk '{$1=""}1').

Getting the time of execution, or the running time, however, is not, unless some action is taken before the command has executed. Once the command is run, nothing can be done - the horses have bolted.

For getting the time of execution, either:

  1. Set the HISTTIMEFORMAT variable, which would make bash save the time of execution in the history, then use text processing to extract the time from the output of history.
  2. Use a wrapper to save the execution time manually.

For getting the running time, again, either:

  1. If you had set HISTTIMEFORMAT before running the command, you could parse the output of history to get the execution time and then calculate the run time.
  2. Use a wrapper to run the command with time: time some-command. Processing the output of time is a bit messy if you want to leave the output of the command untouched.
  3. Use a wrapper to calculate runtime based on manually saved execution time.

With HISTTIMEFORMAT set

If you set the HISTTIMEFORMAT variable, then you need not use a wrapper. First, in your .bashrc, set HISTTIMEFORMAT. You can set it to anything, as long as it's not empty. If you don't want your history command to show the time, you could set it to a space (HISTTIMEFORMAT=" ").

Now, there's an alias present in the default .bashrc of Ubuntu:

# Add an "alert" alias for long running commands.  Use like so:
#   sleep 10; alert
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'

We can adapt it for our purposes:

alert_post () 
{ 
    RET=$?;
    END=$(date '+%s');
    LAST_COMM="$(HISTTIMEFORMAT='%s '; history 1)";
    START=$(awk '{print $2}' <<<"$LAST_COMM");
    START_TIME=$(date -d"@$START" '+%T %D');
    COMM=$(awk '{print $3}' <<<"$LAST_COMM");
    notify-send --urgency=low -i "$([ $RET = 0 ] && echo terminal || echo error)" "$COMM started at $START_TIME finished in $((END - START)) seconds.";
    return $RET
}
alias a=alert_post

You can use it thus:

$ sleep 10; a

Wrapper function

Using a function or a script to wrap around the command:

alert_wrapper () 
{ 
    START=$SECONDS;
    START_TIME=$(date '+%T %D');
    COMM="$1";
    "$@";
    RET=$?;
    END=$SECONDS;
    notify-send --urgency=low -i "$([ $RET = 0 ] && echo terminal || echo error)" "$COMM started at $START_TIME finished in $((END - START)) seconds."
    return $RET
}
alias a=alertwrapper

Then run your command prefixed with a:

$ a sleep 10

Notes:

  1. There are many ways of getting the time, both execution and runtime. I used both the $SECONDS special variable and the date command. You could get by with only the date command.
  2. I have used notify-usd, which should be available on standard Ubuntu. You could look at other ways of producing notifications.
  3. I have saved the exit status of the command in $RET and returned it, so you should be able to rely on exit codes: do-something; a || echo "Couldn't do something." or a do-something || echo "Couldn't do something." will echo if do-something had a non-zero exit status.