As much as I like the Linux command line, there are times when it’s easier to use a point-and-click interface. One such example is when I need to compare two files in different directories. Typing out the full paths on the console can be tedious whereas it would be much simpler to just drag’n’drop two files from a Nautilus window onto a comparison tool. Unfortunately I didn’t know of any such tool – so I wrote one myself.

The architecture

When I first thought about creating a drag’n’drop comparator tool, I feared that I had to create a full-fledged GTK application or something similar. That would have been a lot of effort when all I needed was a GUI wrapper for a command-line operation like cmp /path/to/fileA /path/to/fileB.

Thankfully there is an easier way. All that’s needed for a program launcher is a “desktop entry” or .desktop file according to specification. It’s a text file containing information like the application’s name, icon, and what command to run when the tool is launched. The latter may include field codes like %F which evaluates to a list of filenames for those items drag’n’dropped onto the launcher.

Now that handling drag’n’drop turned out to be easy, the whole tool could be finished just by adding the line Exec=cmp %F to the .desktop file, right? Well, unfortunately it’s not that simple as the result would be a terminal window that pops up and immediately gets closed once the comparison is done. Putting aside that this would be a pretty ugly solution, it also doesn’t contain any kind of error handling. What we need is an additional shell script that handles the program logic and uses Zenity for user interaction. While cmp is still used at the core of the script, its return code is evaluated to present the user with dialogs native to their desktop environment.

Another main part of the script is the evaluation of its input parameters. The user is expected to drag’n’drop two files, but what if they picked a different number of files or none at all? Thanks to Zenity a nice error message will be shown, but there’s one more special case to be handled. As I wrote in the introduction, the main motivation in writing this tool has been to compare two files from different directories via drag’n’drop. Unfortunately it’s impossible to select two files from different directories (i.e., different Nautils windows) at the same time. Therefore it’s necessary to include a way to drag’n’drop two files consecutively. Whenever the script recognizes that only one argument has been passed, it assumes that this scenario is in play and consequently presents the user with a file selection dialog. The user can now navigate to the second file or drag’n’drop it from e.g. a Nautilus window onto the dialog. It took me a while to figure out that the latter requires the file to be dropped somewhere in the file list part of the dialog which then automatically changes to the directory containing the dropped file. Click OK after that and the comparison takes place. If you’re using a non-GNOME system, your mileage may vary.

The code


[Desktop Entry]
Name=Drag'n'drop comparator
Comment=Compares two files drag'n'dropped onto it %F

# Drag'n'drop comparator
# Compares two files which are drag'n'dropped onto the companion file
# dndcmp.desktop. That way all user I/O happens in a GUI fashion by
# means of zenity dialogs. Nevertheless this shell script could be
# executed from the command line as well.

# Check number of input arguments
if [ $# -eq 1 ]
    # Only one input argument. Open file selection dialog to let the user
    # pick the second file
    file2=$(zenity --file-selection --title="Please pick the second file")
    if [ $? -ne 0 ]
        # Abort script if file selection dialog was aborted
        zenity --error --text="Two files required for comparison."
        exit 1
elif [ $# -eq 2 ]
    # Two input arguments
    # Neither one nor two input arguments. Abort script
    zenity --error --text="Please drag'n'drop one or two files."
    exit 1

# Make sure both inputs are regular files
if [ ! -f "$file1" ]
    zenity --error --text="$file1 is not a file."
    exit 2
if [ ! -f "$file2" ]
    zenity --error --text="$file2 is not a file."
    exit 2

# Now do the actual comparison and inform the user about its result
cmp --quiet "$file1" "$file2"
  # exit status:
  # 0 - same
  # 1 - different
  # 2 - trouble
case $? in
    0) zenity --title="Comparison result" --info \
           --text="Both files contain the same data:\n$file1\n$file2"
    1) zenity --title="Comparison result" --warning \
           --text="The two files differ:\n$file1\n$file2"
    *) zenity --title="Comparison result" --error \
           --text="An error occurred while comparing the files:\n$file1\n$file2"

In order to run the tool, you need to mark both files as executable and have in your path. In order to do that, I executed the following commands on the command line:

chmod a+x dndcmp.desktop
sudo mv /usr/local/bin/

Epilogue: Nobody’s perfect

In order to use the Drag’n’drop comparator, dndcmp.desktop has to be somewhere you can drop files onto. This could be the desktop, but I’d prefer to keep things un-cluttered and have the icon appear in the Unity launcher on my Ubuntu system. Unfortunately I was not able to drop files onto the icon once it was in the launcher. Even though I did quite some testing, I couldn’t figure out why the .desktop file for, say, gedit permits files to be dropped onto it while dndcmp.desktop didn’t. If you read this and know of a solution, do let me know, I would greatly appreciate it. Edited on January 8, 2013: For launcher items to accept any kind of file, the MIME type has to be application/octet-stream whereas the previously used all/allfiles doesn’t work. At least that’s my experience on Ubuntu 12.04 “Precise”. The file dndcmp.desktop above has been changed accordingly.