The Wonder and Frustration of 'xdg-open'

I have a multi-OS life: I use Linux exclusively at home, but Mac (by preference) and Windows at work. Using the Mac means that my command line is very similar to Linux, and I live my life at the command line - always have. I occasionally write scripts to smooth over the differences between Linux and Mac. One nice utility that the Mac has is open which - given a filename - opens the file in the appropriate application, whether it's a PDF or a PNG or anything else. Imagine my surprise to find out that Linux has had a similar utility for ... well, it looks like a decade at least. That application is xdg-open. My first thought was that this would allow me to forget which is the default image viewer when I switch - as I often do - between Fedora and Debian.

xdg-open struck me as a long and awkward name, so the obvious thing to do was to create an alias o - that single letter didn't have a command assigned to it yet. And using OS-detection, that letter passes the filename I give it to either xdg-open or open.

# file: ~/bin/isLinux

if command uname -a | command grep Linux > /dev/null
    echo "true"
    exit 0
    echo "false"
    exit 1

The above can be shortened to one line, but I believe in both clarity and overkill so I put the code on multiple lines and also made it return both an exit code and a text statement of "true" or "false". My normal use case is if isLinux >/dev/null ; then ... I created an essentially identical file for isMac - just replace the word "Linux" with "Darwin" on the uname line.

(I am in no way claiming that the above script is the best or authoritative way of determining your local OS: it works for me.)

Which is all great as far as it goes, but controlling xdg-open turns out to be complex, in a typically Linuxy kind of way. I don't know how Mac's open command works: I didn't investigate, but I kind of assume it uses the Resource fork. And because Apple is so authoritative about applications, you really don't get a choice in the matter: I hope you like the application they chose for you. But Linux is Linux: you have infinite choice, if only you can figure it out.

xdg-open uses at least a couple of files to determine what application you want to use to open a file. The system-level file is usually /usr/share/applications/mimapps.list, but you should confirm this for your system. The user file is ~/.config/mimeapps.list for me, but again you should check your system. xdg-mime is used to read or set your application choices: it modifies ~/.config/mimeapps.list on my system.

To find out the mimetype of a particular file:

$ xdg-mime query filetype Install.txt

Which doesn't tell you what will be used to open the file. You do that with:

$ xdg-mime query default text/plain

I concluded very quickly that I was almost always going to want to know both of these things at the same time, so I wrote a script (what's given here is just the core of it, with my checking code and help() function removed):

mimetype="$(xdg-mime query filetype "${1}")"
handler="$(xdg-mime query default "${mimetype}")"
file_out="$(file -b "${1}")"

cat << EOF
'file' out: '${file_out}'
 mime-type: '${mimetype}'
   handler: '${handler}'

The output of file is thrown in for extra interest, because it often presents a different picture than the MIME type.

When I start changing these settings I came across a nasty little stumbling block for control freaks like me. My first instinct was to check ~/.config/mimeapps.list into my git repo of configuration files. Great, soft-link it in there. Then I changed a setting ... wait, did I remember to soft-link it? It's not soft-linked. Oh well, try again. No, it wasn't me: xdg-mime clobbers links (soft or hard). It breaks them, and replaces the file with a new separate copy. For a lot of people this won't matter, but for many of us it's an incredibly infuriating behaviour. And thoroughly un-Unix-like: the "I know better than you" attitude is supposed to be reserved for the user on Linux, not the application.

With that out of the way ...

To set the application to open text/plain files:

$ xdg-mime default xvim.desktop text/plain

That will of course only work if you have a xvim.desktop file somewhere on the path that XDG searches - xvim.desktop is a pointer to a shell script I created to open NeoVim in a new terminal. Like the configuration, the most likely locations for .desktop files are /usr/share/applications/ for the system, or ~/.local/share/applications/ for the user. It's highly recommended you read the Archwiki on "Desktop entries".