How to extend the GNOME Nautilus file manager with custom scripts

Although GNOME, in its 3.x iteration has been the object of many debates, due to its non-traditional desktop paradigm, it is probably the most used desktop on Linux. The default file manager included in GNOME is Nautilus (the application new name is “Files”). In this tutorial we will see how we can extend the file manager with functionalities provided by custom scripts.

In this tutorial you will learn:

  • How to use custom scripts to extend Nautilus functionalities

Software Requirements and Conventions Used

Software Requirements and Linux Command Line Conventions
Category Requirements, Conventions or Software Version Used
System Distribution-independent
Software The Nautilus file manager
Other No specific requirements are needed to follow this tutorial
Conventions # – requires given linux commands to be executed with root privileges either directly as a root user or by use of sudo command
$ – requires given linux commands to be executed as a regular non-privileged user

Creating the scripts directory

The first thing we want to do is to create the directory that will host our scripts:  ~/.local/share/nautilus/scripts. Once placed in this directory, the scripts will automatically appear in the Nautilus context menu displayed when we select one or more files:

$ mkdir -p ~/.local/share/nautilus/scripts

In the command above we used the -p switch (short for --parents) to be sure that all directories in the specified path are created as needed, and no errors are generated if some of them already exists. With our directory in place, we can start working on our very useful scripts: notice that they will be correctly included in the Nautilus context menu only if they are made executable. Before writing code we should learn to know some variables we can use inside the scripts: they are the main way we can interact with the status of the file manager, accessing very useful information.



Nautilus scripts variables

For our scripts to be somehow useful, it should be possible to interact with the file manager status and be able to reference, for example, the path and the names of selected files, or the current working directory: we can access these information via some variables set exactly for this purpose. Let’s see them.

First of all we have the NAUTILUS_SCRIPT_SELECTED_FILE_PATHS variable. As should always happen, the variable name is pretty self-explanatory: this variable holds the full filesystem path of the files currently selected in the file manager. The variable value is a string; the file paths are delimited by the use of newline characters.

Another very useful variable is NAUTILUS_SCRIPT_SELECTED_URIS. We can use this variable, like the one we just saw, to reference selected files, with one difference: the files are not referenced by their paths, but by their URI, or “Unified Resource Identifier”. The role of this variable becomes evident when working on remote filesystems: in that case, simple paths will not work, and the NAUTILUS_SCRIPT_SELECT_FILE_PATHS variable will be empty. In such situations, to access the files we also need to know the type of protocol in use: a file selected in the file manager via the sftp protocol, for example, will be referenced as sftp://path/to/file.

Finally, we have the NAUTILUS_SCRIPT_CURRENT_URI and the NAUTILUS_SCRIPT_WINDOW_GEOMETRY variables. The former contains the URI of the directory opened in the file manger; the latter information about the geometry (width and height) and the position of the file manager window (eg: 631×642+26+23).

A practical example

As an example, we will build a very simple script: its purpose will be that of organize pictures selected in the file manager on the base of their creation date. In this case the script will be written in python, a language that is supported by default on every distribution; we can, of course, also write bash scripts, or use any other supported scripting language.

Nowadays almost all digital images contains metadata we can use to retrieve all kind of information, like the type of camera or device used to create the picture and the settings used. What we are talking about are called exif tags: what interests us in this case is the OriginalDateTime field (36867). The script will be able to organize only the pictures that include that tag, and will rearrange them in directories created using the the “year/month name” pattern. Pictures containing no information will be placed in a directory called “unsorted”. Here is our script, we will save it as “organize.py”:

#!/usr/bin/env python3
"""
Author: Egidio Docile
Organize selected pictures by their creation date, using the exif
DateTimeOriginal tag
"""

import datetime
import os

from PIL import Image

DATETIME_ORIGINAL=36867

def main():
    for path in os.getenv('NAUTILUS_SCRIPT_SELECTED_FILE_PATHS','').splitlines():
        try:
            exif_data = Image.open(path)._getexif()
        except OSError:
            continue

        try:
            date = datetime.datetime.strptime(exif_data[DATETIME_ORIGINAL], '%Y:%m:%d %H:%M:%S')
            directory = os.path.join(date.strftime('%Y'), date.strftime('%B'))
        except (KeyError, ValueError, TypeError):
            directory = "unsorted"

        os.makedirs(directory, exist_ok=True)
        os.rename(path, os.path.join(directory, os.path.basename(path)))

if __name__ == '__main__':
    main()

As you can see, we access and read the NAUTILUS_SCRIPT_SELECTED_FILE_PATHS variable using the os.getenv method, also providing an empty string as the default value, in case the variable is not set. We then used the splitlines method to “explode” the string that is the value of the variable we just mentioned, into a list, using the newline character as delimiter. Finally we processed each file path in a for loop.



Of course the script can be improved, but let’s verify that it works. Once we place it in the ~/.local/share/nautilus/scripts directory, we must make it executable by running:

$ chmod +x ~/.local/share/nautilus/scripts/organize.py

A new entry should appear in the file manager context menu, when files are selected:


script-context-menu-entry

The context-menu entry for our script

And here is our script in action. We select the images we want to sort and click on “script/organize.py” in the context menu:

Using graphical dialogues in the scripts

There may be some cases in which our scripts, to work correctly, should be able to interact with the user, perhaps to ask confirmation before performing an operation. We can create such dialogues in our scripts, depending on the programming language we are using. When writing bash scripts, for example, we can use zenity, a program to create GTK dialogue boxes, that is usually included with a GNOME installation; if it is not we can install it using our favorite distribution package manager. On Fedora, for example we can run:

$ sudo dnf install zenity

On Debian-based distributions, instead we can use apt-get:

$ sudo apt-get install zenity

The package is also included in the “Extra” Archlinux repositories:

$ sudo pacman -S zenity

Let’s see an example on how to use zenity. This time we will write a bash script that, when executed, will lowercase the name of all selected files, after asking for, and receiving, the user confirmation.

#!/bin/bash
set -e
set -u
set -o pipefail

if zenity --question --title="Confirmation" --text="Should I run the script?"; then
  echo "${NAUTILUS_SCRIPT_SELECTED_FILE_PATHS}" | while read -r selected_file; do
     file="$(basename "$selected_file")"
     mv "${file}" "${file,,}"
   done
fi

In the script we invoked zenity with the --question, --title and --text options:
they are used respectively, to display a question dialogue, to set the title of the popup window that will be displayed, and to set the actual dialogue text. In this case, zenity exit code will be 0 if the user clicks on “yes” and 1 if he clicks on the “no” button. As we know, an exit code of 0 means that the command was executed successfully, therefore the code inside the if statement will be executed. To lowercase the file we used the ${parameter,,} parameter expansion.


zenity-dialogue

The zenity dialogue

{loadposition in-article-ads-banner_31}

When using more sophisticated programming languages like python, we can access several type of graphical libraries, to generate dialogues, like TkInter which is the de-facto standard python GUI toolkit, or PyGObject to use the GTK toolkit and libraries.

Conclusions

In this tutorial we saw how, in few easy steps, we can extend the Nautilus file manager using custom made scripts written in various type of programming languages. We saw where the scripts should be placed in the filesystem, and what are the variables we can reference inside them to get the paths or URI of the selected file, the URI of the directory opened in the file file manager and its geometry. Finally we two example, one written in python and the other in bash. In the latter, we saw also how to generate a graphical dialogue using zenity: if you are curious about this utility, stay tuned, we will talk about it soon, here on linuxconfig.org.



Comments and Discussions
Linux Forum