Is there a way to display an icon only while my script is running?

Here is Python launcher, based on this answer and an additional research in Internet, that works nice in Ubuntu 16.04:

#!/usr/bin/env python3
import signal
import gi
import os
import subprocess
import sys
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Gtk, AppIndicator3, GObject
import time
from threading import Thread

# Execute the script
script = os.path.basename(sys.argv[1])
subprocess.Popen(sys.argv[1:])
script_name = script.rsplit('/', 1)[-1]

class Indicator():
    def __init__(self):
        self.app = 'Script indicator'
        iconpath = "/usr/share/unity/icons/launcher_bfb.png"

        self.indicator = AppIndicator3.Indicator.new(
            self.app, iconpath,
            AppIndicator3.IndicatorCategory.OTHER)
        self.indicator.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
        self.indicator.set_menu(self.create_menu())
        self.indicator.set_label("Script Indicator", self.app)
        # the thread:
        self.update = Thread(target=self.show_seconds)
        # daemonize the thread to make the indicator stopable
        self.update.setDaemon(True)
        self.update.start()

    def create_menu(self):
        menu = Gtk.Menu()
        # menu item 1
        item_quit = Gtk.MenuItem('Quit')
        item_quit.connect('activate', self.stop)
        menu.append(item_quit)

        menu.show_all()
        return menu

    def show_seconds(self):
        global script_name
        t = 0
        process = subprocess.call(['pgrep', script_name], stdout=subprocess.PIPE)
        while (process == 0):
            t += 1
            GObject.idle_add(
                self.indicator.set_label,
                script_name + ' ' + str(t) + 's', self.app,
                priority=GObject.PRIORITY_DEFAULT
                )
            time.sleep(1)
            process = subprocess.call(['pgrep', script_name], stdout=subprocess.PIPE)

        subprocess.call(['notify-send', script_name + ' ended in ' + str(t) + 's'])
        time.sleep(10)
        Gtk.main_quit()

    def stop(self, source):
        global script_name
        subprocess.call(['pkill', script_name], stdout=subprocess.PIPE)
        Gtk.main_quit()

Indicator()
# this is where we call GObject.threads_init()
GObject.threads_init()
signal.signal(signal.SIGINT, signal.SIG_DFL)
Gtk.main()
  • If you see any way to improve the script, please, do not hesitate to edit the answer. I haven't much experience with Python.

Create executable file and place the above lines as its content. Let's assume the file is called script-indicator.py. Depending on your needs and your script nature, you can use this launcher in one of the following ways:

./script-indicator.py /path/to/script.sh
./script-indicator.py /path/to/script.sh &
./script-indicator.py /path/to/script.sh > out.log &
./script-indicator.py /path/to/script.sh > /dev/null &
  • Where script.sh is the one you want to indicate.

Screenshot made just when the script.sh is ended:

enter image description here

  • Click on the image to see an animated demo.

Alternatively you can place the script in /usr/local/bin to be accessible as shell command system wide. You can download it from this dedicate GitHub repository:

sudo wget -qO /usr/local/bin/script-indicator https://raw.githubusercontent.com/metalevel-tech/powerNow/master/powerNow.py
sudo chmod +x /usr/local/bin/script-indicator

I've tested it with the following syntax:

script-indicator /path/to/script.sh
script-indicator /path/to/script.sh &
script-indicator /path/to/script.sh > output.log
script-indicator /path/to/script.sh > output.log &
script-indicator /path/to/script.sh > /dev/null
script-indicator /path/to/script.sh > /dev/null &
nohup script-indicator /path/to/script.sh >/dev/null 2>&1 &
# etc...

It's sufficient enough to have a script like so:

#!/usr/bin/env bash

while true
do
    if pgrep -f gedit > /dev/null
    then

       printf "\r%b" "\033[2K"
       printf "Script is running"
    else
       printf "\r%b" "\033[2K" 
       printf "Script is not running."
    fi
    sleep 0.25
done

This is a very typical way and often used in shell scripts. What this does, is running continuously, and every quarter of a second it checks if your script is running via pgrep -f. if..fi statements in shell scripts operate on exit status of commands, so pgrep -f returning successful exit status means it found your script's process and it's running. Otherwise, we go to else statement.

In both cases we print a line of text that tells the user if it's running or not. There's also added printf "\r%b" "\033[2K" for printing an escape sequence for clearing the line(just for cleaner output).

From there, the sky is the limit. You can pass that text to another process in terminal via pipe or you can pass the output from the monitoring script to indicator-sysmonitor, which allows displaying custom information in the indicator. It can be installed via

sudo add-apt-repository ppa:fossfreedom/indicator-sysmonitor
sudo apt-get update
sudo apt-get install indicator-sysmonitor

After that just configure the indicator to display a custom command which would be the monitoring script. Example of configuring a custom command can be found here.

Of course, one could write their own indicator in Python, but this might be just an overkill when there's a tool for that already.


With osd_cat

You can use osd_cat from the xosd-bin Install xosd-bin package, e.g.:

<<<"runs" osd_cat -d5 -i20 -o50 -f"-*-*-*-*-*-*-100-*-*-*-*-*-*-*" && firefox

This displays “runs” with font size 100 for 5 seconds at position 20,50 on the screen and starts firefox when it’s ready – you don’t need sleep with this approach. You can use xfontsel to get the X Logical Font Descriptor (this weird -*-*-… thingy) for the -f option, e.g. if you want to use a different font. Read man osd_cat for more options.

With yad

You could use yad Install yad as follows:

yad --title=runs --text="it’s running!" --timeout=5 --button=Fire:1 --button=Abort:0 || firefox

This has the advantage that you can abort the command or execute it immediately, if you do nothing it will close after 5 seconds in this example.


You can display a pop-up notification when the process starts. Just change your script to

notify-send "Your image will arrive" "after 5 minutes" && sleep 300 && firefox file:///home/tasks/fiveminutes.jpg

You can set the duration of the notification bubble with the -t parameter (and time in milliseconds) with the notify-send command, for example to set the duration for 5 minutes use

notify-send -t 300000 "heading" "text"

but this parameter may be ignored completely depending on your desktop environment (see this).