Command-line hacking: Assigning folder icons to directories


While most of my "command-line hacking" articles apply generally to Linux systems, this one is specific to the Gnome desktop. The problem to be solved is the bulk application of folder icons to directories, in the Nautilus file manager. Here is what Nautilus looks like with folder-specific icons assigned:

The documented way to set these icons is to use Nautilus itself -- there is a "Preferences" dialog box which allows a file to be chosen from a graphical file selector. That's fine, for one or two directories; but what if you what to apply folder icons to hundreds of folders in one operation?

Of course, needing to do this pre-supposes that you already have folder icons for all these directories. The usual reason to have them is that you're using some software that writes folder icons. Many audio players do this -- they will extract images from audio files, or download them from sites like Amazon. The Calibre e-book reader does this also -- if an e-book is imported into the library, and the e-book meta-data indicates a cover image, the image is extracted as a file.

The complication is that different applications use different names for the folder image. Common examples are cover.jpg, folder.jpg, and .folder.jpg. The file type need not necessarily be JPEG; PNG and GIF files are also common. If icon files are to be processed automatically, the utility should be able to look for many different conventional image filenames, without user intervention.

Using "gio" to set icons

To set the folder icon at the command line, you can use the gio utility, like this:

$ gio set -t string {directory} metadata::custom-icon file://{icon_path}

Both the directory and the icon path must reference absolute filenames in the filesystem.

For the record, the default folder icon can be restored by removing the property:

$ gio set -t unset {directory} metadata::custom-icon

The challenge is to find out which file represents the folder icon, and call gio in the appropriate way. Incidentally, gio does not add or modify any files in the selected directory -- it only affects Gnome meta-data; that means that changes made by running gio typically affect only one user.

Iterating potential folder icons

The gnome-set-folder-icon utility maintains a list of potential filenames, and potential file extensions, and iterates both lists, concatenating the elements into a single filename. As soon as a matching file is found, it is assigned using gio.

NAMES=( cover folder Folder .folder )
EXTS=( .png .jpg .gif )

dir= # Name of directory to process

# Outer loop -- iterate candidate names
for name in "${NAMES[@]}"
  # Inner loop -- iterate candidate extensions 
    for ext in "${EXTS[@]}"
       # Form a candidate filename from the directory, name, and extension
       if [ -f "$test_icon_path" ] 
         # Found the icon file... process it

# If we get here, no folder icon was found

Once the matching folder icon is found it, and the directory name, have to be expanded to full paths, which is simple enough.

  full_dir=`realpath "$dir"`
  full_icon_path=`realpath "$test_icon_path"`
   gio set -t string "$full_dir" "metadata::custom-icon" \

Iterating the command-line arguments

The logic to set the icon file is wrapped up inside a function called do_directory(), that takes the directory name as an argument. We would like to be able to set multiple directory names on the command line, so we can do something like:

$ gnome-set-folder-icon /path/to/audio/folders/*

That's easily done in a Bash script:

for arg in "$@"
  do_directory "$arg"

Note the double-quotes here, and the liberal use of such quotes throughout the script -- it's very likely that some directories will have spaces in their names. Preventing spaces being treated as command-line argument delimiters is a perennial problem in shell scripting.

Except for a bit of error checking, the code above is the whole impmentation.


However, if you're interested, the full source for gnome-set-folder-icon is available from my GitHub repository.