# Resolving the path to a Bash script

I wrote a function to resolve the full path to the current Bash script, and I'm wondering if it's SOLID:

#!/usr/bin/env bash

scriptpath()
{
result=${BASH_SOURCE[0]} # unfortunately readlink -f isn't available on Macs, # so resolve the symlinks manually while [ -h "$result" ]
do
symdir=$(dirname "$result")
cd -P "$symdir" result=$(readlink "$result") done echo "$result"
}

echo "The path to the current script is: $(scriptpath)"  Is there anything I may have looked over in the function? I'm not sure because my answer seems quite different than the solutions listed here or here, and I'm pretty fuzzy on symlinks. I basically want to know if my function (scriptpath) is any different than this on a Linux system: readlink -f "${BASH_SOURCE[0]}" | xargs -0 dirname

• Use -L instead of -h in your while test as -h has been deprecated (they want to drop it but it would break scripts so it remains for now. Probably since -h is so commonly used for help.) – DocSalvager May 3 '16 at 9:55
• How can I get the behavior of GNU's readlink -f on a Mac? seems to cover this issue pretty well. – DocSalvager May 3 '16 at 10:03

### Let's make this testable

With a small twist, the function can become testable. Instead of finding the real path of the current script, it would be better to generalize it to find the real path of any path:

real_path()
{
result=$1 # ... }  ### Fails to follow relative symlinks correctly Now that the script is testable, here's a test it will not pass: mkdir -p /tmp/a touch /tmp/a/b (cd /tmp/a; test -L /tmp/a/c || ln -s ../a/b c) echo$(real_path /tmp/a/c)


The output will be ../a/b instead of the expected /tmp/a/b. Basically if there are relative links in the chain of links recursively followed, the script will break.

### Alternative implementation

This is what I use in production, and I haven't seen a corner case yet where it breaks:

real_path() {
target=$1 ( while true; do cd "$(dirname "$target")" target=$(basename "$target") test -L "$target" || break
target=$(readlink "$target")
done

echo "$(pwd -P)/$target"
)
}


Notice that the portion where the function changes the working directory is wrapped within a (...) sub-shell. This is probably overkill, but I think it's good to eliminate side effects as a general principle.