Linux readlink Command: Inspect Symbolic Link Targets
readlink is a command that outputs the target path stored in a symbolic link file. It is useful for checking whether the link contains a relative path or where a broken link points.
GNU readlink also supports path normalization with -f, -e, and -m, but GNU Coreutils recommends using realpath, which has clearer functions and naming, for general path normalization.
What is the readlink command?
When you specify a symbolic link as an argument, it outputs the target string stored in the link.
readlink current
If the current link points to releases/2026-09, it will output as follows.
releases/2026-09
This value may be a relative path interpreted based on the location of the link. The default readlink shows it close to the stored form without converting the target string to an absolute path.
Basic Syntax and Usage
readlink [options] file...
You can first create an example link with ln -s and then check it.
ln -s releases/2026-09 current readlink current
If you specify a regular file or directory without any options, there is no link target value, so nothing is output, and it fails with a non-zero exit status.
readlink /etc/passwd printf '%s\n' "$?"
Understanding Relative and Absolute Links
Link Storing a Relative Path
The target of a relative link is interpreted based on the directory where the link is placed, not the current terminal location.
ln -s ../shared/config.yml /srv/app/config.yml readlink /srv/app/config.yml
The output is ../shared/config.yml. This path is resolved relative to /srv/app and points to /srv/shared/config.yml.
Link Storing an Absolute Path
ln -s /srv/releases/2026-09 /srv/current readlink /srv/current
An absolute link outputs the target string starting from the root, like this.
/srv/releases/2026-09
Absolute links have a clear location, but they can easily break if the file tree is moved to a different path. Choose relative or absolute links considering your deployment structure and portability.
Checking Broken Symbolic Links
Even if the target file does not exist, the basic readlink can read the stored target string as long as the symbolic link itself remains.
ln -s missing-target broken-link readlink broken-link
missing-target
The existence of the link target can be checked separately as follows.
if [ -L broken-link ] && [! -e broken-link ]; then printf '%s\n' 'This is a broken symbolic link.' fi
-L checks the existence of the link itself, while -e checks the existence of the target it points to. Permission issues or link cycles may also affect the result.
Path normalization options
GNU readlink also provides a mode to normalize paths in addition to outputting the link target string.
| Option | Action |
|---|---|
-f, --canonicalize |
All path components except the last must exist, and symbolic links and dot components are resolved. |
-e, --canonicalize-existing |
All components including the last item must exist. |
-m, --canonicalize-missing |
Nonexistent components are also assumed to be directories to normalize the path. |
Print a canonical path with -f
readlink -f /srv/current/config.yml
Outputs the absolute path with symbolic links and ., .. resolved. The last component may be missing, but the preceding components must exist.
Require every component to exist with -e
readlink -e /srv/current/config.yml
The entire path, including the target file, must exist for the result to be output. It does not prevent a race condition where the target changes between existence check and actual use.
Allow missing components with -m
readlink -m /srv/future/../uploads/new-file.txt
Handles intermediate components that do not exist, so it can be used for path planning or string arrangement. There is no guarantee that the output location can actually be created.
Handling multiple links and output separation
Check multiple symbolic links
GNU readlink can handle multiple links at once.
readlink current previous config-link
Each target string is output line by line. If the correspondence between input and output is important, it is clearer to display the link name along with the result in a loop.
for link in current previous config-link; do target=$(readlink -- "$link") || continue printf '%s -> %s\n' "$link" "$target" done
The difference between -n and -z
-n or --no-newline omits the trailing separator character when processing a single link. Using it with multiple files may generate warnings.
readlink -n current
-z or --zero outputs a NUL character instead of a newline at the end of each result. In automation where filenames and link targets may contain newlines, -z is clearer.
readlink -z current previous
Comparison of readlink, realpath, ls, and stat
| Command | Main Purpose | Features |
|---|---|---|
readlink |
Check the target string stored in a link | Even broken links can be read as the link itself |
realpath |
Filename normalization and relative path calculation | GNU is primarily recommended for path normalization |
ls -l |
Check links and targets in the list in a way that's easy for humans to read | Displayed together with other list information, making it unsuitable for parsing |
stat |
Check detailed metadata of the link or target | You can check inode, permissions, time, and file type |
If only the target string of the link is needed in automation, readlink is appropriate. If the purpose is general path normalization and relative path conversion, realpath is clearer.
Using in shell scripts
Check whether the link target is a relative path
target=$(readlink -- "$link") || {
printf '%s\n' 'Cannot read the symbolic link.' >&2
exit 1
}
case "$target" in
/*) printf '%s\n' 'This is an absolute path link.';;
*) printf '%s\n' 'This is a relative path link.';;
esac
To calculate a relative path to its actual location, it should be based on the directory containing the link. Simply appending it to the current working directory can lead to an incorrect path.
Check the exit status
Check whether the command succeeded rather than only checking if the output is empty. While the link target cannot be an empty string, this expresses the intention of error handling more clearly.
if target=$(readlink -- "$link"); then printf '%s\n' "$target" else printf 'Failed to read the link: %s\n' "$link" >&2 fi
Summary of major options
| Option | Description |
|---|---|
-f, --canonicalize |
Normalize the path whose components exist except for the last item. |
-e, --canonicalize-existing |
All path components must exist for success. |
-m, --canonicalize-missing |
Treat non-existent components as directories. |
-n, --no-newline |
Omit the output delimiter after a single result. |
-z, --zero |
Terminate each result with a NUL character. |
-q, -s, --quiet |
Suppresses most error messages. |
-v, --verbose |
Displays error messages. |
Common issues
Fails without any output.
In default mode, the specified path may not be a symbolic link or the link itself may be inaccessible. Check the exit status and, if necessary, display error messages with -v.
The relative link target appears to be incorrect.
The output relative path should be interpreted based on the directory containing the link. It is not based on the current directory from which the command is executed.
readlink -f does not work on other systems.
Normalization options, including -f, have implementation differences. In scripts that require portability, check whether the target operating system supports it, and if your goal is path normalization, also compare available realpath implementations and options.
FAQ
Does readlink read the content of the file that a symbolic link points to?
No. In its default mode, it outputs the target path string stored in the link file. It does not read the content of the target file.
Can broken symbolic links also be checked with readlink?
If the link itself exists and can be read, you can check the stored target string with the default readlink. The -e normalization only succeeds if the actual target exists.
Relative paths are output. How can I check the absolute path?
In a GNU environment, you can use readlink -f link, but for general path normalization, using realpath is clearer in terms of functionality and intent.
Why use readlink instead of ls -l?
ls -l is suitable for human-readable listings, while readlink only outputs the target string, making it convenient for scripts. In automation, be sure to also handle exit status and filename separation.









