Log in

08 August 2010 @ 01:30 pm
Snippet for handling GUI-invoked shell scripts  
I write a lot shell scripts that are meant to get invoked via GNOME's "open with" function. As such, the script needs to handle things I'd normally rely on the commandline shell invocation to handle. For example, say "html_pretty_print" is a tool to rewrite an HTML file, removing extraneous white space and doing proper hierarchical indenting so it's readable. I want to run it on an HTML file, so that I save the original but put the results back into a file with the same name as the original.

Typically, if I were typing at the command line, I'd do something like this:

$ html_pretty_print data.html > data-pretty.html; mv data.html data.html-bak; mv data-pretty.html data.html

Now the original has been backed up and the reformatted version has the original file name. I consider html_pretty_print a well-behaved shell utility because it makes no assumptions about where the user wants to put the output. (In fact, the actual html_pretty_print can also read it's input from standard input rather than reading it from the file handed to it on the command line, making it deal as a pipe-based filter, but that fact isn't relevant to the matter at hand.)

Anyway, the above command line is how I'd do it manually because it captures the state of the process better (in case I walk away in the middle of the process for some reason) If I were writing a script to totally automate the process, I'd probably do it this way:

$ mv data.html data.html-bak; html_pretty_print data.html-bak > data.html;

Fewer moves and no need for a temp file.

But if I'm using GNOME or another window manager's file explorer, I want to left-click on an HTML file and select Open With then html_pretty_print, and have all that work done for me.

When you do an "Open With", your script gets passed the full path name to the file you clicked on as the first argument. However, the current working directory could be anywhere (typically your home directory), so when your script writes out a file like the temp file, you had better be very specific as to where it wants to put the file, and the backup file. That is, you need to make sure all files are referenced with absolute path names. Usually, the right place is to put it in the same directory as the original file.

Here's a code snippet for devising the name of the backup file such that it gets put in the same directory as the original. I could modify html_pretty_print itself, but I generall prefer to do it in a wrapper. That way I can continue to use html_pretty_print as a well-behaved command line utility, and the wrapper approach works even when the command you're using can't be modified.


i="$1"; # $1 will include full path when invoked by GNOME
test -z "$i" && exit 1;
j=`basename "$i"`-bak;
jdir=`dirname "$i"`;

mv "$i" "$j"

html_pretty_printer "$j" > "$i";

Since $j and $i are full qualified file names, this wrapper can be called from any working directory. The variable jdir can be used to (presumably) to store any temp files you might want. I'd name this wrapper something like "openwith-html_pretty_printer".

Obviously, you can build in a lot more logic if you want a single script to handle both cases. I haven't figured out a reliable way to tell if you've been invoked by Open With yet. I bet you could figure it out if you spend some quality time with some test cases and the "env" command.

The snippit uses "basename" and "dirname" command line utilities common to all Linux and most Unix distributions. Note that "basename" can also strip off file endings, so if you wanted to you could do a variant where instead of turning "data.html" to "data.html-bak", you could change it to "data.bak" by replacing the third line with:

j=`basename "$i" .html`.bak

A real example of wanting to do that is if you're, say, coverting an AVI file to FLV.
(Deleted comment)
(Deleted comment)
jdimpsonjdimpson on August 8th, 2010 11:45 pm (UTC)
Not having posted to my own blog in over a year should answer that question!

It never really occurred to me to get involved in the community aspect of LJ, my main motiviation was to get my scripts documented and disseminated.

That seems stupid in retrospect, if I really do want others to see my stuff. Thanks for your comments.