Ubuntu – Put emblem on folder icon if it contains particular file/subdirectory


How can I create a script and system rule for all directories, that will apply emblem onto every directory's icon if the folder contains .git direct subfolder?
I have already included github emblem icon to my emblems like this

The output effect should be as so:

enter image description here

Best Answer


NOTE: this is a new and updated version of the script, for old version see the edit history of this answer

The script presented below allows to either set a github repository icon or emblem (but not both), as well as reset that metadata back to default. Usage is very simple, as explained by the -h option:

$ ./set_folder_icon.py -h                                                                   
usage: set_folder_icon.py [-h] [-i ICON] [-e EMBLEM] [-r ROOT] [-u]

Finds and sets github repository icons.Choose either --icon or --emblem.
Without either the script exists with 0 exit status.For --icons, use absolute
or relative path.For --emblem use single string of text.Emblem pathnames are
in the format emblem-name.extension.The script then can becalled with -e
<name>.Use ~/.local/share/icons folder for custom emblems (if it does not
exist - create it. Store filenames in specified format

optional arguments:
  -h, --help            show this help message and exit
  -i ICON, --icon ICON  path to image to set
  -e EMBLEM, --emblem EMBLEM
                        single-string emblem name
  -r ROOT, --root ROOT  where search starts,default - current working
  -u, --unset           unset both emblem and icon. Cannot be used with -e or
                        -i options

So if we wanted to recursively search home folder for all github repositories and set their emblem, we'd do set_folder_icon.py -e myemblemname -r $HOME or just cd; set_folder_icon.py -e myemblemname

For best performance, please create ~/bin directory and store the script there as set_folder_icon.py. If you want to use it immediately, run source ~/.bashrc. If you're using a different shell, ensure that your ~/bin is added to $PATH variable.

NOTE: The script can take any arbitrary file with --icon option, but --emblem requires you to have a specific file to be stored in ~/.local/share/icons with filename something like emblem-github. See the The script in action section for an example

The script in action

I happen to keep all my github repositories under ~/GIT folder. In the screenshots below you can see how all github repositores get their emblem set to the custom one.


enter image description here


enter image description here


The script is also available on my personal GitHub repository , where it is more likely to get newer changes and fixes.

#!/usr/bin/env python3
Author: Sergiy Kolodyazhnyy
Date: 7/12/2017
Purpose: Script to find github repository folders
         and set their icons for Ubuntu.
Written for: https://askubuntu.com/q/935003/295286
import argparse
import os
import sys
import subprocess
import urllib.parse

def puke(message, status_code):
    """ Print to stderr and exit with given code """
    sys.stderr.write('>>> OOOPS. Something is wrong:\n')
    sys.stderr.write(message + '\n')

def run_cmd(cmdlist):
    """ Reusable function for running external commands """
        stdout = subprocess.check_output(cmdlist)
    except subprocess.CalledProcessError as cpe:
        puke('Called command failed with ' + str(cpe.returncode) +
             ' exit status\n' + repr(cpe), 4)
        if stdout:
            return stdout

def set_icon(directory, icon_type, image):
    """ Wrapper function that specifies command and calls run_cmd()"""
    key = 'metadata::' + icon_type
    meta = {'metadata::custom-icon': 'string',
            'metadata::emblems': 'stringv'}

    # Because custom-icons and emblems type are mutually
    # exclusive, we need to unset custom-icon to enable emblems
    if icon_type == 'emblems':
        cmd = ['gvfs-set-attribute', '-t', 'unset',
               directory, 'metadata::custom-icon']

    cmd = ['gvfs-set-attribute', '-t', meta[key],
           directory, key, image]
    return run_cmd(cmd)

def unset_all(directory):
    for key in ['metadata::custom-icon', 'metadata::emblems']:
        run_cmd(['gvfs-set-attribute', '-t', 'unset', directory, key])

def find_directories(tree_root):
    """ Does the job of recursive traversal of given directory tree,
        starting at the specified tree root directory. If condition for
        given subdirectory is met, calls set_icon() function"""
    for current_dir, subdirs, files in os.walk(tree_root):
        # This check can be adapted to other cases
        if '.git' in subdirs:
            print('Found', current_dir)
            yield current_dir

def parse_args():
    """ Parses command-line arguments """

    text = ['Finds and sets github repository icons.',
            'Choose either --icon or --emblem. Without either'
            ' the script exists with 0 exit status.',
            'For --icons, use absolute or relative path.',
            'For --emblem use single string of text.',
            'Emblem pathnames are in the format',
            ' emblem-name.extension.', 'The script then can be',
            'called with -e <name>.Use ~/.local/share/icons folder',
            ' for custom emblems (if it does not ',
            'exist - create it. Store filenames in specified format']

    arg_parser = argparse.ArgumentParser(description="".join(text))
    arg_parser.add_argument('-i', '--icon', help='path to image to set',
                            type=str, required=False)
    arg_parser.add_argument('-e', '--emblem', help='single-string emblem name',
                            type=str, required=False)
    arg_parser.add_argument('-r', '--root', help='where search starts,' +
                            'default - current working directory',
                            default='.', type=str, required=False)
    arg_parser.add_argument('-u', '--unset', help='unset both emblem ' +
                            'and icon. Cannot be used with -e or -i options',
                            action='store_true', required=False)
    return arg_parser.parse_args()

def main():
    """ Script entry point """
    # Parse command-line arguments and check their correctness
    args = parse_args()
    status_code = {'icon_missing': 1, 'root_missing': 2,
                   'root_isnt_dir': 3, 'exclusion': 4,
                   'not_string': 5, 'conflict': 6}

    if args.unset and (args.icon or args.emblem):
        puke('Conflicting options', status_code['conflict'])
    if not args.unset:
        # both or none are given
        if not args.icon and not args.emblem:
        if args.icon and args.emblem:
            puke('Can only use either --icon or --emblem',
        # Verify correctness of either one
        if args.icon and not os.path.exists(args.icon):
            puke('Icon pathname does not exist',
        if args.emblem:
            if '/' in args.emblem:
                puke('Emblem must be a single string of text,no paths',
            if not isinstance(args.emblem, str):
                puke('Given argument for emblem is not a string',

        # Verify correctness of the path
        if not os.path.exists(args.root):
            puke('Root pathname does not exist',
        if not os.path.isdir(args.root):
            puke('Root pathname is not a directory',

    if args.unset:
        for directory in find_directories(args.root):
            print('Unsetting', directory)

    # Everything should be OK past this point

    if args.icon:
        meta_type = 'custom-icon'
        icon = 'file://' + urllib.parse.quote(os.path.abspath(args.icon))
    if args.emblem:
        meta_type = 'emblems'
        icon = args.emblem

    # Now do the actual traversal and icon-setting
    for directory in find_directories(args.root):
        set_icon(directory, meta_type, icon)

if __name__ == '__main__':

Automating the script

Of course, we can run the script manually each time (in fact I'd likely make a function that can combine git clone and running this script), although I would recommend that you make the script run each time you log into your GUI session. In order to do that, open the Startup Applications menu, and add the script ( using full path and full filenames is recommended ) as an entry.

enter image description here

And next time you log-in your icons will be set.

As for manual idea, you could run git clone and the script one after the other with && operator, or better yet make a function in ~/.bashrc for that like so:

$ tree

0 directories, 0 files
$ typeset -f my_gitclone
my_gitclone () 
    git clone "$1" && ~/GIT/sergrep/set_folder_icon.py -e github
$ my_gitclone https://github.com/SergKolo/sergrep
Cloning into 'sergrep'...
remote: Counting objects: 387, done.
remote: Compressing objects: 100% (17/17), done.
remote: Total 387 (delta 10), reused 17 (delta 5), pack-reused 365
Receiving objects: 100% (387/387), 115.65 KiB | 0 bytes/s, done.
Resolving deltas: 100% (203/203), done.
Checking connectivity... done.
Found ./sergrep