Determining the absolute absolute path of a shell script

In the course of working on projects like server-side Objective-J, jack, and now narwhal, I’ve often had to write shell scripts that needed to know their location in the filesystem. Rather than hardcoding it, I prefer to infer it automatically at runtime. Unfortunately this isn’t as easy as you would expect.

If the script is invoked with an absolute path (“/foo/bar/baz”) or from your PATH (“baz”), then “$0” in the script will contain the absolute of the script (“/foo/bar/baz”). However, if it is invoked using a relative path (“./bar/baz” from “/foo”) then $0 will contain the relative path (“./bar/baz”). Furthermore, if the path to the script is actually a symbolic link, you’ll get the symlink’s path instead of the original.

Surprisingly, I couldn’t find a definitive solution that handles all these cases, so I took the various ones I did find and created one which I think handles all the cases I’m aware of:

If you don’t want to resolve the symlinks remove the second half.

Embedding and loading a JNI library from a jar

When I searched for ways to load a JNI library from a jar there were numerous hints of how to do it, but no code that I could find. So here’s my solution:

import java.net.URL;
import java.util.zip.ZipFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;

public abstract class UnixDomainSocket {

    static {
        try {
            // get the class object for this class, and get the location of it
            final Class c = UnixDomainSocket.class;
            final URL location = c.getProtectionDomain().getCodeSource().getLocation();
            
            // jars are just zip files, get the input stream for the lib
            ZipFile zf = new ZipFile(location.getPath());
            InputStream in = zf.getInputStream(zf.getEntry("libunixdomainsocket.jnilib"));
            
            // create a temp file and an input stream for it
            File f = File.createTempFile("JARLIB-", "-libunixdomainsocket.jnilib");
            FileOutputStream out = new FileOutputStream(f);
            
            // copy the lib to the temp file
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0)
                out.write(buf, 0, len);
            in.close();
            out.close();
    
            // load the lib specified by it’s absolute path and delete it
            System.load(f.getAbsolutePath());
            f.delete();
            
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(1);
        }
    }
    
    // …
}

This particular example is for JUDS.

It could be extended to load one of several libraries for different architectures, .jnilib or .dylib for Mac OS X, .so for Linux, and .dll for Windows.

This seems like a lot of hoops to jump through, but I couldn’t find an easier way to do it. If you know of a better way, please let me know in the comments.