Resolve Linux Paths Safely with realpath
You will use GNU realpath to turn a pathname into an absolute, normalised name, understand what happens to symlinks and .., and choose the right behaviour when a path is missing. The installed command on this machine is GNU coreutils 9.4, from Ubuntu package version 9.4-3ubuntu6.3.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need a shell and a path you can read. The examples only inspect names and print results. They do not create, remove or modify files, and they do not need elevated privileges.
1. Check the installed command
Confirm which executable the shell will run, then record its version:
$ command -v realpath
/usr/bin/realpath
$ realpath --version
realpath (GNU coreutils) 9.4
Checkpoint: if command -v prints nothing, realpath is not available through your current PATH. Do not replace it with an unverified script that happens to have the same name. Check your distribution's coreutils package instead.
2. Resolve an ordinary path
Pass one or more pathnames as arguments. By default, every component except the last one must exist, and the default physical mode expands symlinks as it encounters them:
$ realpath /var/log/../log/syslog
/var/log/syslog
The result is written to standard output, one resolved name per input. The command does not change the path on disk. A successful result is a useful value to pass to another command, but it is not proof that the last component is a regular file. A final component may be a directory, a device, or another object allowed by the filesystem.
Use the status immediately after a check if a script needs to distinguish success from failure:
$ realpath /var/log/syslog
/var/log/syslog
$ printf '%s\n' "$?"
0
3. Choose how missing components are handled
The default is a middle ground: parent components must exist, but the final component may be absent. Use --canonicalize-existing when every component, including the last one, must exist:
$ realpath --canonicalize-existing /var/log/syslog
/var/log/syslog
$ realpath --canonicalize-existing /path/that/does/not/exist
realpath: /path/that/does/not/exist: No such file or directory
The second command returns non-zero. In a script, do not use its printed error as the test. Branch on the status and decide whether a missing path is expected.
Use --canonicalize-missing when you are preparing a name before creating it, or when no component is required to exist:
$ realpath --canonicalize-missing /tmp/example/new/../result.txt
/tmp/example/result.txt
This option only calculates a name. It does not create /tmp/example or result.txt. If a later command creates a file, handle that command's permissions and race conditions separately.
4. See the symlink boundary
Physical resolution is the default, but logical resolution changes when .. is interpreted relative to a symlinked directory. Make a small test tree in a scratch directory if you need to see the difference:
$ work=/tmp/realpath-demo
$ mkdir -p "$work/real/child"
$ ln -s "$work/real" "$work/link"
$ realpath --physical "$work/link/child/../child"
/tmp/realpath-demo/real/child
$ realpath --logical "$work/link/child/../child"
/tmp/realpath-demo/link/child
Here the physical result follows link before resolving the parent reference. Logical mode resolves the .. using the written path first, so the symlink remains in the result. The difference matters when a path crosses a symlinked deployment directory. If you have no deliberate logical-path requirement, the default physical behaviour is usually the less surprising choice.
Warning
The mkdir and ln lines above change state in /tmp. Use a unique scratch directory, and do not point them at a directory containing useful data. If you want to undo this particular test after checking it, remove only that scratch directory with rm -rf -- /tmp/realpath-demo. Review the path before running a recursive removal.
5. Produce a relative name or retain symlinks
--relative-to=DIR resolves the input and prints its name relative to the supplied directory:
$ realpath --relative-to=/var /var/log/syslog
log/syslog
The base directory is an argument to the calculation, not a directory change. Both the base and the input are resolved for this comparison. If the input is outside the base, the output can contain ...
--relative-base=DIR is useful when you want compact names only for paths below a known tree. Paths below the base are printed relative to it; other paths remain absolute:
$ realpath --relative-base=/var/log /var/log/syslog /etc/hosts
syslog
/etc/hosts
Use --strip, also spelled --no-symlinks, when you want normalised absolute syntax without expanding symlink components:
$ realpath --strip /var/log/../log/syslog
/var/log/syslog
Do not confuse this with checking the physical destination. A stripped result can still name a symlink, so use the default or --physical when the resolved target is what matters.
6. Make scripts quiet and binary-safe
--quiet suppresses most diagnostics, which is useful when a script reports its own error. It does not make an invalid path successful:
if resolved=$(realpath --quiet --canonicalize-existing -- "$input"); then
printf 'Using %s\n' "$resolved"
else
status=$?
printf 'Cannot resolve input (status %s)\n' "$status" >&2
exit "$status"
fi
The -- marks the end of options, so a filename beginning with a hyphen is treated as a pathname. The example deliberately uses command substitution for a single result. For multiple arbitrary filenames, use --zero and a NUL-aware consumer, because ordinary newline-separated output cannot represent a newline inside a filename safely:
$ realpath --zero -- /path/to/one /path/to/two | od -An -t x1
2f 70 61 74 68 2f 74 6f 2f 6f 6e 65 00
Do not parse output with whitespace splitting when filenames may contain spaces, tabs or newlines. Also remember that realpath resolves names; it does not grant access. A later open can still fail, and a path can change between resolution and use.
Done means
- You checked that the installed command is GNU coreutils 9.4 or identified the version you are documenting.
- You selected default physical resolution, logical resolution, or symlink stripping deliberately.
- You used
--canonicalize-existingfor strict existence checks and--canonicalize-missingonly when absent components are acceptable. - Your script checks the exit status and uses
--before untrusted or hyphen-leading pathnames. - You use
--zerowith a NUL-aware consumer when filenames cannot safely be separated by newlines.