Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

POSIX compliant readlink -f

POSIX compliant readlink -f implementation for POSIX shell scripts.


The readlink and the realpath commands are not specified POSIX and some environments may not be installed. There are many implementation alternatives to readlink -f ([1], [2], [3]). Some of them probably work, but It has some edge case bugs, unresolved issues, no tests, poor performance, long code, bashism, non-POSIX compliant and license problem. This feature is important to access files on relative paths from the main script. I couldn't find any reliable code despite of.


Source code:

Short code version has been temporarily removed. See v1.0.0 if you need it.


  1. varname=$(readlinkf_* "<path>") (assign to variable)
  2. (readlinkf_* "<path>") (output to stdout)
  3. readlinkf_* "<path>" (output to stdout, not recommended)

Note: Usage 1 and 2 use subshells and therefore have no side effects. It does not change the current directory and any variables. Usage 3 has side effects.

The maximum depth of symbolic links is 40. This value is the same as defined in Linux kernel 5.6. (See MAXSYMLINKS) However, readlink -f has not limitation. If you want to change, modify max_symlinks variable in these function.

1. readlinkf_posix

This implementation uses cd -P and ls -dl.

How it works

It parsing the output of ls -dl and resolve symbolic links.

ls -dl: "%s %u %s %s %u %s %s -> %s\n",
  <file mode>, <number of links>, <owner name>, <group name>,
  <size>, <date and time>, <pathname of link>, <contents of link>

The format of ls -dl is specified in POSIX.

If the file is a symbolic link and the -L option is not specified, this information shall be about the link itself and the <pathname> field shall be of the form:

"%s -> %s", <pathname of link>, <contents of link>

2. readlinkf_readlink

This implementation uses cd -P and readlink (without -f).

Note: Probably fast than readlinkf_posix, but readlink is not POSIX compliant. It may not be installed in some environments.


readlinkf cannot handle filenames that end with a newline. It is very rare case, therefore I chose performance and code simplicity.

About the test

Test Results

Tested with ash (busybox), bosh, bash, dash, ksh, mksh, posh, yash, zsh on Debian. And tested on macOS, FreeBSD, Cygwin, Msys2 and Git BASH. The tests are compared with the result of GNU readlink -f (greadlink) command.

If you want to test yourself, use script. Root privilege is required for edge case test around the root directory. Therefore using Docker by default for safely create files on the root directory.

./ [SHELL (default:sh)] [Dockerfile] [DOCKER-TAG (default: latest)]

Note: The readlink built into busybox is not compatible with readlink of GNU coreutils.

./ ash dockerfiles/alpine 3.11 # will fails

If you want to test without Docker, set ALLOW_CREATION_TO_THE_ROOT_DIRECTORY environment variable. Check script for what it do before running it.



  • v1.0.0: 2020-05-13 Short code version
  • v1.1.0: 2020-06-20: Friendly version


Creative Commons Zero v1.0 Universal. Feel free to modified or embed in your script.