Bash shell trick : play a sound when your computations fail or succeed

Several times, i launched heavy computations from a bash terminal, and then worked on something else waiting for them to finish. And then 3 hours later, discover that they failed at the first minute.

Sometimes it’s the opposite : i wait for them to finish, but… they already finished.

Solution : Make bash play a sound when a heavy command succeeds or fails !

1) Play a sound from the terminal

We will use the program aplay.

$ aplay -q ~/Public/my_sound.wav

So, i downloaded some free scores in wav format and edited them to cut and extract just a piece of Haendel’s Hallelujah (for successful jobs) and Chopin’s Funeral March (for failed jobs).

$ aplay -q ~/Public/hallelujah.wav
$ aplay -q ~/Public/funeral.wav

2) Get the return code of a command

Bash has a variable « ? » that stores the return code of the last executed command. After running something, you can try:

$ echo $?

If it returns 0, it means that everything went well (no errors). Otherwise, the value is supposed to help you to understand what the problem was using the command’s documentation (it is often -1, 1, 2 …).
So we can store this value and then test it to play the right sound. (We need to store it, because our bash code will also modify the « ? » variable).

if [ $LAST -eq 0 ]; 
    then aplay -q ~/Public/hallelujah.wav; 
    else aplay -q ~/Public/funebre.wav; 

3) Set it permanently

We need to add the previous code to the $PROMPT_COMMAND variable, which contains the instructions executed after a command to restore a command prompt (like displaying $PS1, etc…).

PROMPT_COMMAND='LAST=$?; if [ $LAST -eq 0 ]; then aplay -q ~/Public/hallelujah.wav; else aplay -q ~/Public/funebre.wav; fi'

To get a permanent effect everytime you open a terminal, edit your .bashrc file (nano ~/.bashrc) and add the line at the end.
Note that on some terminals, the PROMPT_COMMAND is overwritten by /etc/profile.d/ I partially solved the problem by editing /etc/profile.d/ and commenting the line that sets PROMPT_COMMAND.

4) Restrict it to very-long command only

As it is annoying to play 15 seconds of sound at every cd and ls, we need to restrict the execution to long commands.
To measure the running time of a command we need to compute the difference between the time when it started and the time when it exited.
We get the current time this way (number of seconds since January the first 1970):

$ printf "%(%s)T" -1

We can store the result of printf into a variable with printf -v:

printf -v __timer_start "%(%s)T" -1
printf -v __timer_stop "%(%s)T" -1

We already know how to execute it at the end of a command, it’s using PROMPT_COMMAND. We will add the second line into it.
To execute a command when another command starts, we will use the trap DEBUG linux command:

$ trap 'something to execute' DEBUG

We can use it to execute something at the begining of every command.

Then, we will store the difference between __timer_start and __timer_stop into a variable __timer (using printf -v again), which we will test to know if we should play a sound or not (example : if [ $__timer -gt 10 ] for ten seconds threshold).

5) All together

Modify your .bashrc to include the two following lines (the trap, and the PROMPT_COMMAND definition):

trap '[ -v __timer_start ] || TZ=UTC printf -v __timer_start "%(%s)T" -1' DEBUG

PROMPT_COMMAND='LAST=$?; TZ=UTC printf -v __timer_stop "%(%s)T" -1; TZ=UTC printf -v __timer $((__timer_stop - __timer_start)); if [ $__timer -gt 10 ]; then if [ $LAST -eq 0 ]; then aplay -q ~/Public/hallelujah.wav; else aplay -q ~/Public/funeral.wav; fi; fi; unset __timer_start'

To make it work, you need to unset the $__timer_start variable at the very end.
The TZ=UTC are only used to define the time-zone (not so useful).

Here it is !
Open a terminal.

persalteas@persalstation[~]$ ls
Data       labo           logo_univ.png   Modèles   Release.key  signature.png  Téléchargements    Bureau    Documents    Libraries      mcfold.htm      Projects    Public    Seafile      Software    Zotero
persalteas@persalstation[~]$ sleep 11
persalteas@persalstation[~]$ sleep 11; ls aiefv
ls: impossible d'accéder à 'aiefv': Aucun fichier ou dossier de ce type

The first command (ls) plays nothing. The second (sleep 11) takes 11 seconds and plays Hallelujah. The last (sleep 11; ls aiefv) takes 11 seconds, fails, and plays the Funeral March.

Enjoy !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.