How to obtain the absolute path of a file via Shell (BASH/ZSH/SH)?
#! /bin/sh
echo "$(cd "$(dirname -- "$1")" >/dev/null; pwd -P)/$(basename -- "$1")"
Use realpath
$ realpath example.txt
/home/username/example.txt
Forget about readlink
and realpath
which may or may not be installed on your system.
Expanding on dogbane's answer above here it is expressed as a function:
#!/bin/bash
get_abs_filename() {
# $1 : relative filename
echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
}
you can then use it like this:
myabsfile=$(get_abs_filename "../../foo/bar/file.txt")
How and why does it work?
The solution exploits the fact that the Bash built-in pwd
command will print the absolute path of the current directory when invoked without arguments.
Why do I like this solution ?
It is portable and doesn't require neither readlink
or realpath
which often does not exist on a default install of a given Linux/Unix distro.
What if dir doesn't exist?
As given above the function will fail and print on stderr if the directory path given does not exist. This may not be what you want. You can expand the function to handle that situation:
#!/bin/bash
get_abs_filename() {
# $1 : relative filename
if [ -d "$(dirname "$1")" ]; then
echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
fi
}
Now it will return an empty string if one the parent dirs do not exist.
How do you handle trailing '..' or '.' in input ?
Well, it does give an absolute path in that case, but not a minimal one. It will look like:
/Users/bob/Documents/..
If you want to resolve the '..' you will need to make the script like:
get_abs_filename() {
# $1 : relative filename
filename=$1
parentdir=$(dirname "${filename}")
if [ -d "${filename}" ]; then
echo "$(cd "${filename}" && pwd)"
elif [ -d "${parentdir}" ]; then
echo "$(cd "${parentdir}" && pwd)/$(basename "${filename}")"
fi
}
Try readlink
which will resolve symbolic links:
readlink -e /foo/bar/baz