Sound when bash command is ending

bash, command-line, notifications, shell

Solution

I had the same question and thanks to @rici's answer I was able to come up with a working solution. You can join PROMPT_COMMAND into a single line if you want, or leave it like this:

trap '_T=${_T:-$SECONDS}' DEBUG
PROMPT_COMMAND='
    ((SECONDS - _T > 10)) &&
        { play ~/done.wav &disown;}&>/dev/null;
    unset _T
'

This combination of settings will play `~/done.wav` whenever a command-line takes more than 10 seconds to complete. It will not play the sound just by leaving your terminal idle.

On OS X the command to play a sound is `afplay` and a few sounds can be found in `/System/Library/Sounds`

But the version above cannot tell between long-running batch commands, for which we want a sound, and interactive ones, such as your choice of terminal editor, man pages, `|less`, and such.

Here's a version that does tell, 99% of the time, by checking the last commandline against a regexp with one's list of common interactive programs or pipes:

trap '_T=${_T:-$SECONDS}' DEBUG
PROMPT_COMMAND='
    if ((SECONDS - _T > 10)); then 
        l=$(history 1);
        [[ ! ${l:7} =~ (^vim|^man|^ssh|less$) ]] && 
        { play ~/done.wav &disown;}&>/dev/null;
    fi;
    unset _T
'

You should adjust the regexp to taste. Again, you can join PROMPT_COMMAND into a single line if you want, I've left the `;` where they should be on a single-liner.

This is still efficient, because:

- the shell doesn't spawn any external process while evaluating either hook (except for the obvious sound playing, which is launched in the background);

- the DEBUG hook is very fast; I believe it doesn't even access the "magic variable" $SECONDS (which probably forces a syscall to get the current time) more than twice per command-line, in the first DEBUG and in PROMPT_COMMAND to compare the values;

- the history check and regexp is only done if the command took more than N seconds.

How it works

To understand how it works, you can set these debug prints:

trap 'echo DEBUG' DEBUG
PROMPT_COMMAND='echo PROMPT_COMMAND'

and then pay attention to when they are called:

PROMPT_COMMAND
$ sleep 2; sleep 2
DEBUG
  (1st sleep takes its time)
DEBUG
  (2nd sleep takes its time)
DEBUG
PROMPT_COMMAND
$ (user waits before pressing enter)
DEBUG
PROMPT_COMMAND
$

In light of this, my settings above:

- unset _T whenever a prompt is shown to the user—at the end of `$PROMPT_COMMAND`;

- if _T is unset, which happens in the first `DEBUG` of every command-line, right after you press enter; then set it to the current time (using `$SECONDS` which is more efficient than launching an external `date` process);

- at the next prompt, compare _T with the current time and play the sound if needed, launching the play command in the background and discarding any output (including `[1] PID` and other job-related info.)

Problem

When I start a long command in bash I need for signal sounded at its command completion. How it to make in .bashrc for all long command? I have alsa for sounds. Search in google without results.

Original source