Desktop notification when long running commands complete

I'd like to have a desktop notification whenever a command that has run for more than, say 15 seconds, finishes in an interactive shell.

In other words, I would like all commands to be wrapped in something like this

start=$(date +%s);
ORIGINAL_COMMAND;
[ $(($(date +%s) - start)) -le 15 ] || notify-send "Long running command finished"

What's the best way to accomplish this in bash?


As far as I understood you want a wrapper. And you want to use a command through it so that it will give you desired notification if running time of your command is more than 15 sec. So here is it.

wrapper(){
    start=$(date +%s)
    "$@"
    [ $(($(date +%s) - start)) -le 15 ] || notify-send "Notification" "Long\
 running command \"$(echo $@)\" took $(($(date +%s) - start)) seconds to finish"
}

Copy this function in your ~/.bashrc and source ~/.bashrc as,

. ~/.bashrc

Useage

wrapper <your_command>

If it takes more than 15 sec you will get the desktop-notification describing the command and its time of execution.

Example

wrapper sudo apt-get update

screenshot of desktop-nitification


In ~/.bashrc there is an alias alert defined as:

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$//'\'')"'

which can be used to notify the completion of command execution.

Usage:

$ the_command; alert

e.g.

$ sudo apt-get update; alert

snap1

You may customize the alias as per your need and desire.


You want https://launchpad.net/undistract-me (installable from the Ubuntu archives with sudo apt-get install undistract-me) which does precisely what you're asking for, including working automatically (that is, without having to remember to add something extra to potentially long-running commands).


Apart from a wrapper like souravc suggested, there isn't really any good way to do this in bash. You can hack your way around it with a DEBUG trap and a PROMPT_COMMAND. A DEBUG trap is triggered whenever you run a command, and PROMPT_COMMAND is run just before the prompt is written.

So stuff for ~/.bashrc becomes something like

trap '_start=$SECONDS' DEBUG
PROMPT_COMMAND='(if (( SECONDS - _start > 15 )); then notify-send "Long running command ended"; fi)'

This is a hack, so don't be surprised if you encounter odd side-effects with this.


EDIT

TL;DR: create autocompletion shortcut in .inputrc and function in .bashrc . Run command as usual, type in, but instead of ENTER, press the shortcut that you specified in .inputrc

The person who placed bounty on this question said:

"All of the existing answers require typing an additional command after the command. I want an answer that does this automatically."

While researching the solutions to this problem I've stumbled upon this question from stackexchange, which allows binding CtrlJ to a sequence of commands: Ctrla (move to beginning of line), place "mesure" string in front of the command you entered, Ctrlm (execute)

Thus you get functionality of auto-completion and separate ENTER command for measuring time, while perserving original purpose of the second function i posted bellow.

As of now, here are the contents of my ~/.inputrc file:

"\C-j": "\C-a measure \C-m"

And here are the contents of .bashrc (note , I haven't been using bash in forever - I use mksh as my shell , hence that's what you see in the original post. Functionality is still the same)

PS1=' serg@ubuntu [$(pwd)]
================================
$ '
function measure () 
{

/usr/bin/time --output="/home/xieerqi/.timefile" -f "%e" $@ 

if [ $( cat ~/.timefile| cut -d'.' -f1 ) -gt 15 ]; then

    notify-send "Hi , $@ is done !"

fi


}

Original Post

Here's my idea - use a function in .bashrc. Basic principle - use /usr/bin/time to measure the time it takes for command to complete, and if it is over 15 seconds, send notification.

function measure () 
{

if [ $( /usr/bin/time -f "%e" $@ 2>&1 >/dev/null ) -gt 15 ]; then

    notify-send "Hi , $@ is done !"

fi


}

Here I am redirecting output to /dev/null but to view output, redirecting to file can also be done.

A much better approach, IMHO, is to send output of time to some file in your home folder (just so you don't pollute your system with timefiles, and always know where to look). Here's that second version

function measure () 
{

/usr/bin/time --output=~/.timefile -f "%e" $@ 

if [ $( cat ~/.timefile | cut -d'.' -f1 ) -gt 15 ]; then

    notify-send "Hi , $@ is done !"

fi


}

And here's the screenshots of first and second version, in that order

First version, no output enter image description here

Second version, with output enter image description here