A handy bash function for creating "file://" URLs

I often find myself loading test files from my local filesystem into Firefox with "file://" URLs.  Unfortunately Firefox on OS X (or at least my local build of 3.1) is maddeningly inconsistent about recognizing an argument as a filesystem path or a URL.  So for example "ffz test.html" ("ffz" is my launch script) usually opens "file:///Users/cbartley/Dev/mozilla/test.html" (which I want) but sometimes tries to open "http://www.test.html/" (which doesn't even make sense).

It seemed sensible to switch to simply supplying a full file URL on the command line so there's no confusion.  The downside is that file URLs need to be absolute and constructing an absolute file path on the fly is a pain.  Fortunately, I can make the computer do this work for me with a handy bash function.  The "fileurl" function below takes a path to a file (usually a relative path) and prints the corresponding "file://" URL for it.  So for example:
  fileurl ../test.html
might print
  file:///Users/cbartley/dev/mozilla/test.html

I can invoke my test build with something like
  ffz `fileurl ../test.html`
and have it reliably open the file every time.

I haven't yet rolled fileurl into the ffz script, but that's the logical next step.  I should also acknowledge that there may well be better ways to do this than the approach I've used -- I am no bash expert.  Finally, for all those other amateur bash programmers out there, the magical Google phrase for finding documentation on the weird things you can do with variables in bash is "variable mangling".

# Given a path to a file (relative or absolute), print a "file://" URL for
# that file.
fileurl()
{
  # Split the directory name out of the argument path.
  #   "/dir/subdir/file" ==> "/dir/subdir"
  #   "dir/subdir/file" ==> "dir/subdir"
  #   "subdir/file" ==> "subdir"
  #   "file" ==> "."
  TEMP="/$1"                            # Hack: Prepend a slash so there's at least one
  TEMP="${TEMP%/*}"                     # Chop off the trailing "/file"
  TEMP="${TEMP#/}"                      # Remove the leading slash if it's stil there
  DIRNAME="${TEMP:-.}"                  # If DIRNAME is empty, set it to "."

    # Get the base file name from the argument path.
  BASENAME="${1##*/}"                   # Remove everything up to the last slash, inclusive

    # Convert the directory name to an absolute path.
  ABSDIRNAME=$(cd "$DIRNAME"; pwd)

    # Echo the file URL built from the components.
  echo "file://$ABSDIRNAME/$BASENAME"
}
12 responses
Five seconds with google gets me to
echo file://`readlink -f $1`
@chris:

Here's what I got when I tried that:

cbartley:~/dev/mozilla/src$ echo file://`readlink -f ../test.html`
readlink: illegal option -- f
usage: readlink [-n] [file ...]
file://

This is on OS X. Linux readlink behavior might be different.

Works fine on Ubuntu, ah well...
I would:
fileurl()
{
if [ "`echo $1 | cut -c1`" = "/" ]; then
file=$1
else
file=`pwd`/${1}
fi
echo "file://${file}"
}

Both solutions will break if there is a space on the filename.

The difference to my solution is:
the absolute path to a file is the same as the absolute path to your current dir plus the relative path.

About the readlink solution... you might try:
-f, or --canonicalize, or -e, or --canonicalize-existing

or anything its manual say will canonicalize the parameter.

While you may have good reasons for using a 'launch script' (such as using a debugging build of Firefox), if you're just trying to open the file in your default brower, the 'open' command [on OS X] is surely easier.

`open foo.html` *ought* to work as expected. I use this routinely for boring html files. However, I might consider adapting your script to make 'localhost' URLs for dev work I do on my MacBook. That might be exceptionally useful!

Also, if you check the man page for `open,` you'll find that you can specify which application it opens with, which could be adapted to a very simple alias. (e.g. `open -a /path/to/debugging-firefox.app $1)

How about “ffz file://`realpath test.html`” ?
@asrail:

It turns out fileurl will handle spaces if you ignore the fact that it doesn't URL encode them. Firefox seems to take file URLs with spaces in it OK even though strictly speaking they're malformed. On the command line you'd need to quote the file URL of course.

On OS-X readlink doesn't seem to support -f at all, or --canonicalize or anything like that. The related "stat" command does support "-f" but I can't find any format option to -f which will print an absolute path, which is too bad.

@John Silvestri:

I believe that the big deficiency I found with the open command is that there is no way to pass arbitrary arguments to Firefox. For example, my ffz script does something like

"$absolute-path-to-firefox-bin" -P Test "$@"

I'm pretty sure that there's no way to pass "-P Test" to Firefox when using "open". I'd love for someone to prove me wrong on this though.

@Pavel Anossov:

There appears to be no realpath command on OS X, although I will certainly want to remember it next time I'm working on Linux!

$ touch /tmp/a\ test
$ fileurl /tmp/a\ space
file:///tmp/a space

FX interprets this as two arguments and try to open "file:///tmp/a".

At least on Linux.

@Curtis Bartley
That's sad :) realpath() should be in libc, the realpath program would be little more than

#include <stdlib>

int main(int argc, char** argv)
{
char resolved[1024];
if (argc > 1)
{
if ( realpath(argv[1], resolved) )
{
printf("%s\n", resolved);
}
else {
printf("Error at: %s\n", resolved);
}
}
// else print usage
}

Try to compile it :)

function fileurl() { (unset CDPATH; cd "`dirname "$1"`"; echo "file://`pwd`/`basename "$1"`") }
CDPATH causes cd to echo the new directory on my system which allows this alternative
function fileurl() { (CDPATH=.; echo "file://`cd "\`dirname "$1"\`"`/`basename "$1"`") }
both versions tested with both file and directory names containing spaces.