linux-commands

Linux realpath Command: Resolve Paths Safely

Linux realpath Command: Resolve Paths Safely

The Linux realpath command converts a path into a canonical path, usually an absolute one with . and .. cleaned up and symbolic links resolved. For example:

realpath ./logs/../reports/ticket.txt

If the current directory is /home/alex, it may print:

/home/alex/reports/ticket.txt

That is useful when a ticket, script, or command gives you a relative path and you need to know which location it actually refers to. It is also useful for spotting symbolic links before you edit, copy, or upload the wrong file.

realpath does more than tidy text. By default, GNU realpath checks the filesystem while resolving path components. That difference matters when compared with commands such as dirname and basename.

Quick realpath command reference

TaskCommand
Print an absolute canonical pathrealpath ./reports/ticket.txt
Require the complete path to existrealpath -e ./reports/ticket.txt
Allow missing path componentsrealpath -m ./future/report.txt
Keep symbolic links unexpandedrealpath -s ./current/config.yml
Print a path relative to a directoryrealpath --relative-to=/srv ./bundle.zip
Protect a path beginning with -realpath -- ./-report.txt

The basic form is:

realpath [OPTION]... FILE...

GNU realpath accepts multiple files and prints one result per input. Options vary across operating systems, so check realpath --help or man realpath before putting GNU-specific flags into a portable script.

Convert a relative path to an absolute path

Suppose a support ticket says an export was written to ./output/inventory.csv. The path is incomplete without its working directory. Run:

pwd
realpath ./output/inventory.csv

Example output:

/home/alex/support-case-4821
/home/alex/support-case-4821/output/inventory.csv

Now the ticket note can contain the exact location instead of “the file is somewhere under output.”

Relative paths are resolved from the current working directory, not from the directory containing your shell script. This is a common source of bugs in cron jobs and support scripts. A command that works from your home directory may point somewhere else when automation starts in / or another configured directory.

When the starting directory matters, confirm it:

printf 'Working directory: %s\n' "$PWD"
realpath -- ./output/inventory.csv

Quoting the path keeps spaces and wildcard characters inside one argument. The -- ends option parsing, so a name beginning with a dash is treated as a path rather than an option.

What realpath changes

A canonical path removes avoidable navigation components:

realpath /srv/support/./cases/../bundles/report.zip

Possible output:

/srv/support/bundles/report.zip

The command handled three things:

  • . meant “this directory” and disappeared.
  • cases/.. canceled out during path resolution.
  • The relative or messy input became one absolute path.

If symbolic links appear in the path, the default physical mode resolves them as they are encountered. For example, suppose /srv/app/current is a symbolic link to /srv/app/releases/2026-07-29:

realpath /srv/app/current/config/app.yml

It can print:

/srv/app/releases/2026-07-29/config/app.yml

That output answers a practical question: which release would you actually edit?

Check whether every path component exists with -e

Use -e or --canonicalize-existing when the whole path must already exist:

realpath -e -- /srv/support/case-4821/bundle.zip

If the file or any parent directory is missing, the command returns a nonzero status and prints an error. That makes -e useful before reading, hashing, copying, or uploading an existing file:

bundle=${1:-}

if ! resolved_bundle=$(realpath -e -- "$bundle"); then
  printf 'Bundle path does not exist: %s\n' "$bundle" >&2
  exit 1
fi

printf 'Using bundle: %s\n' "$resolved_bundle"

This proves the path resolved at that moment. It does not prove the file is readable, safe, or unchanged. Test the file type and access separately:

if [ ! -f "$resolved_bundle" ] || [ ! -r "$resolved_bundle" ]; then
  printf 'Bundle is not a readable regular file\n' >&2
  exit 1
fi

The Linux test command guide covers checks such as -f, -d, -r, and -w.

Allow a path that does not exist with -m

Use -m or --canonicalize-missing when you are planning a destination that has not been created yet:

realpath -m -- /srv/reports/next-week/../daily/inventory.csv

Output:

/srv/reports/daily/inventory.csv

With -m, no path component needs to exist. That is helpful when previewing an output path before a script creates directories or files.

It is not an existence check. This is a mistake:

if realpath -m -- "$output"; then
  echo "The output exists"
fi

The command can succeed precisely because -m permits missing components. Use [ -e "$path" ], [ -f "$path" ], or [ -d "$path" ] when existence matters.

Before creating a destination, validate the intended base directory:

output=/srv/reports/daily/inventory.csv
resolved_output=$(realpath -m -- "$output")

case "$resolved_output" in
  /srv/reports/*) ;;
  *)
    printf 'Refusing unexpected destination: %s\n' "$resolved_output" >&2
    exit 1
    ;;
esac

mkdir -p -- "$(dirname -- "$resolved_output")"

That guard helps catch a bad variable before it creates a cleanly named file in the wrong part of the server. Path-prefix checks need careful design for security-sensitive software, but even a simple allowlisted base is better than blindly trusting ticket input.

Use -s, --strip, or --no-symlinks when you want an absolute normalized path without expanding symbolic links:

realpath -s -- /srv/app/current/../current/config/app.yml

This can keep /srv/app/current in the output instead of replacing it with the release directory it targets.

Compare both views during troubleshooting:

printf 'Logical name: %s\n' "$(realpath -s -- /srv/app/current/config/app.yml)"
printf 'Actual target: %s\n' "$(realpath -e -- /srv/app/current/config/app.yml)"

The logical name is useful in documentation and service configuration. The resolved target is useful when you need to inspect the real file. Neither view is universally better; they answer different questions.

The Linux symbolic links guide explains how to inspect links with ls -l and readlink.

realpath -L versus -P

GNU realpath defaults to -P, or physical resolution. It follows symbolic links as it encounters them, then processes later path components.

-L, or logical resolution, processes .. before resolving symbolic links. The results can differ when .. appears after a symbolic-link component:

realpath -P -- /srv/app/current/../shared/config.yml
realpath -L -- /srv/app/current/../shared/config.yml

Do not choose -L because “logical sounds nicer.” Use the default physical behavior unless you specifically need shell-style logical path handling and have tested the directory layout. Paths that combine links and .. are easy to misread during an incident.

Sometimes an absolute path is correct but awkward in a report. GNU realpath can print a path relative to a known directory:

realpath --relative-to=/srv/support -- /srv/support/case-4821/logs/app.log

Output:

case-4821/logs/app.log

This is useful for archive manifests, deployment reports, and ticket attachments where every file lives under one known base.

If the target is outside the base, the result can contain ..:

realpath --relative-to=/srv/support -- /etc/hosts

Possible output:

../../etc/hosts

Treat that as a warning when your workflow expects files to remain inside /srv/support. Do not assume relative output automatically means safe output.

These commands overlap, but they are not interchangeable:

CommandBest question it answers
realpath FILEWhat canonical absolute path does this input resolve to?
readlink LINKWhat target text is stored in this symbolic link?
dirname PATHWhat directory portion remains after removing the final component?
basename PATHWhat is the final path component?
pwdWhat is the shell’s current working directory?

dirname and basename are primarily path-text tools. They can process a path that does not exist. realpath normally consults the filesystem and resolves links. pwd reports one directory; it does not resolve an arbitrary file argument.

Use the command that matches the question instead of stacking several commands until the output looks familiar.

Common realpath mistakes

Assuming the default requires the final file to exist

On GNU systems, default realpath requires all but the final component to exist. Use realpath -e when the final file must exist too.

Using -m as validation

realpath -m allows missing components. It normalizes a planned path; it does not confirm the destination exists.

Forgetting the working directory

realpath ./report.txt resolves from the process’s current directory. Log pwd or use a known absolute base in automation.

Default output may point at a release directory, mounted location, or shared target rather than the link name shown in a ticket. Compare realpath -s with realpath -e when the distinction matters.

Forgetting portability

GNU options such as -m, --relative-to, and long option names may not behave the same on macOS, BusyBox, or other Unix-like systems. Check the target system before shipping a script.

A practical help desk example

This script accepts a support-bundle path, resolves it, confirms it is a readable regular file, and refuses files outside the approved intake directory:

#!/usr/bin/env bash
set -u

bundle=${1:-}

if [ -z "$bundle" ]; then
  echo "Usage: $0 BUNDLE_PATH" >&2
  exit 2
fi

if ! resolved=$(realpath -e -- "$bundle"); then
  printf 'Bundle not found: %s\n' "$bundle" >&2
  exit 1
fi

case "$resolved" in
  /srv/support/intake/*) ;;
  *)
    printf 'Bundle is outside the intake directory: %s\n' "$resolved" >&2
    exit 1
    ;;
esac

if [ ! -f "$resolved" ] || [ ! -r "$resolved" ]; then
  printf 'Bundle is not a readable regular file: %s\n' "$resolved" >&2
  exit 1
fi

printf 'Resolved bundle: %s\n' "$resolved"
stat --format='Size: %s bytes\nModified: %y' -- "$resolved"

The script resolves symbolic links before checking the allowed location. That prevents a link under the intake directory from quietly targeting a file elsewhere. For hostile multi-user systems, stronger race-resistant application code may be necessary, but this pattern is a solid improvement for ordinary internal support automation.

Practice realpath without touching production

Build a throwaway directory tree with a normal file, a missing destination, and a symbolic link. Compare default realpath, -e, -m, and -s. Then run the same commands from two working directories and watch relative input resolve differently.

You can practice these path-handling habits in Shell Samurai. It is cheaper to learn why a path changed in a practice terminal than in a backup script pointed at the wrong directory.

Frequently asked questions

What does realpath do in Linux?

realpath prints a canonical path, normally as an absolute path with . and .. processed and symbolic links resolved. GNU realpath checks path components according to the option you choose.

Does realpath require the file to exist?

By default on GNU/Linux, all path components except the final one must exist. Use -e to require every component, including the final file, or -m to allow every component to be missing.

Yes, by default. Use realpath -s when you want path normalization without expanding symbolic links.

How do I get an absolute path in Linux?

Run realpath -- FILE. For a path that must already exist, use realpath -e -- FILE. Remember that relative input is interpreted from the current working directory.

On GNU/Linux they often produce similar canonical paths, but their option sets and portability differ. readlink is also used to print the target stored in a symbolic link, while realpath is named and documented around canonical path resolution. Check the local manuals before relying on either in cross-platform scripts.

Practice This in a Real Terminal

Shell Samurai gives you safe Linux missions so the commands actually stick. Chapter 1 is free; the full practice path is a one-time purchase, not another subscription.