694

Question: is there a simple sh/bash/zsh/fish/... command to print the absolute path of whichever file I feed it?

Usage case: I'm in directory /a/b and I'd like to print the full path to file c on the command-line so that I can easily paste it into another program: /a/b/c. Simple, yet a little program to do this could probably save me 5 or so seconds when it comes to handling long paths, which in the end adds up. So it surprises me that I can't find a standard utility to do this — is there really none?

Here's a sample implementation, abspath.py:

#!/usr/bin/python
# Author: Diggory Hardy <diggory.hardy@gmail.com>
# Licence: public domain
# Purpose: print the absolute path of all input paths

import sys
import os.path
if len(sys.argv)>1:
    for i in range(1,len(sys.argv)):
        print os.path.abspath( sys.argv[i] )
    sys.exit(0)
else:
    print >> sys.stderr, "Usage: ",sys.argv[0]," PATH."
    sys.exit(1)
ivanleoncz
  • 9,070
  • 7
  • 57
  • 49
dhardy
  • 11,175
  • 7
  • 38
  • 46
  • I would argue that @DennisWilliamson's answer (using -m option) is superior for (usually) being more portable and working with files that don't exist. – TTT Apr 25 '14 at 12:11
  • Or Flimm's answer; both are good solutions. Bannier's however answers _my_ original question best. – dhardy May 08 '14 at 12:59
  • 3
    OSX users: see [this answer](http://stackoverflow.com/a/3572105/2063546) – Ian May 29 '14 at 19:19
  • Possible duplicate of [Bash: retrieve absolute path given relative](https://stackoverflow.com/questions/4175264/bash-retrieve-absolute-path-given-relative) – Evandro Coan Jul 05 '19 at 04:25

23 Answers23

947

Use realpath

$ realpath example.txt
/home/username/example.txt
Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Benjamin Bannier
  • 55,163
  • 11
  • 60
  • 80
  • 187
    +1, very nice program. But note that it is an external command (not a shell built-in) and may not be present in every system by default, i.e. you may need to install it. In Debian it resides in a separate package with the same name. – Roman Cheplyaka Oct 12 '10 at 17:22
  • Exactly what I was looking for, thanks! Only question is why I couldn't find this myself... – dhardy Oct 13 '10 at 15:38
  • True, it isn't a built-in, but that's usually only an issue when using locked-down university computers. – dhardy Oct 13 '10 at 15:48
  • 5
    I added to my path a `realpath` consisting of: https://github.com/westonruter/misc-cli-tools/blob/master/realpath – Weston Ruter Jan 24 '12 at 01:10
  • 1
    @Flimm: It will still give you a valid path given some relative path. If you want to know about file existence use another tool like e.g. test. – Benjamin Bannier Feb 27 '13 at 17:47
  • @honk: no, it doesn't. `realpath file_that_does_not_exist` just gives me this error: "file_that_does_not_exist: No such file or directory" I'm using realpath version 1.17 on Ubuntu. – Flimm Feb 27 '13 at 17:59
  • @Flimm: OK, I didn't know that. I use the implementation from GNU coreutils 8.21. – Benjamin Bannier Feb 27 '13 at 18:58
  • 17
    @Dalin: Try installing coreutils. The binary is named grealpath. – Toni Penya-Alba Feb 04 '14 at 09:08
  • 9
    This tool was introduced in GNU coreutils 8.15 (in 2012), so it's quite new. – marbu Feb 06 '14 at 22:29
  • @Flimm, how exactly would you expect to get a full path of a file that does not exist? – Vassilis Dec 21 '17 at 08:39
  • @Vassilis: easy, it would give me the path of that file were it to exist. For instance, `realpath .bashrc` could return `/home/username/.bashrc` whether or not `.bashrc` exists. – Flimm Dec 21 '17 at 12:47
  • @Flimm, I disagree my friend. The system cannot know where a file could exist! OK, the .bashrc is a very common file, but what about `myfile.txt`? – Vassilis Dec 23 '17 at 06:03
  • @Vassilis Let's say I run `vim myfile.txt` on the command-line. How does Vim know where to look? `myfile.txt` could be anywhere on the filesystem! Vim knows to look in the current directory, it knows how to convert the relative path to an absolute path, *before knowing whether the file exists or not*. If the file doesn't exist, Vim will open a new file at that absolute path. It is not confused, it always knows how to convert a relative path to an absolute path, whether or not the file exists. – Flimm Dec 24 '17 at 10:41
  • @Flimm, you example with `.bashrc` confused me, I thought you expected the full path even if you weren't in home directory. Now I see what you mean! – Vassilis Dec 26 '17 at 18:39
  • 1
    realpath exists on osx (aka MacOS), as of 2018, or earlier probably – Purplejacket Oct 17 '18 at 18:39
  • 2
    @Purplejacket High Sierra does not have it, even v10.13.6. So must be since Mojave. – ivan_pozdeev Nov 11 '18 at 15:43
  • 1
    This doesn't do _quite_ what the OP asked for: it also resolves any symlinks in the path. – ivan_pozdeev Nov 11 '18 at 15:45
  • 1
    In order to NOT TO resolve symlinks, you need to do `realpath -s filename.txt` – nagylzs Oct 11 '19 at 13:08
  • 1
    macOS Catalina 10.15.4 (with zsh, but shouldn't make a difference) has NO realpath :-( – Walter Tross May 04 '20 at 20:01
  • 7
    @WalterTross, it is available from the `coreutils` formula via Homebrew. – Benjamin Bannier May 05 '20 at 08:46
  • For me on debian required this first `sudo apt-get install realpath` FWIW... – rogerdpack Aug 20 '20 at 20:56
  • @Flimm with more recent versions, realpath works also with non-existent files or directories. Looking at realpath from coreutils 8.32, it has options `-e, --canonicalize-existing all components of the path must exist` and `-m, --canonicalize-missing no path components need exist or be a directory`, so `realpath foo_file` returns `/current_dir/foo_file` and `realpath -m bar_dir/foo_file` returns `/current_dir/bar_dir/foo_file` even when the file or directory don't exist. You can still use `realpath -e` if you want to check if they exist instead. – Dario Seidl Jun 15 '22 at 14:29
375

Try readlink which will resolve symbolic links:

readlink -e /foo/bar/baz
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
  • 64
    I would rather use '-f' instead of '-e' so that we can see absolute path of a nonexistent file. – hluk Oct 12 '10 at 14:54
  • 5
    This looks like the simplest to me, while also being portable. readlink is port of the gnu coreutils, so it will be installed on almost any linux distribution, except for some embedded ones. – catphive Nov 16 '10 at 00:15
  • 20
    It seems to me that `-m` is the best option to use. – Matt Joiner May 21 '11 at 07:02
  • 2
    @Dalin: `/usr/bin/readlink` on Mavericks. Or did you mean those options are not found on OS X? It seems to be a stunted BSD version... :p – iconoclast Jul 24 '14 at 16:13
  • 2
    hluk: `-f` option is also the one to use in OpenBSD – mykhal Aug 08 '14 at 08:26
  • 17
    @iconoclast Those options aren't available on OSX, and as per the BSD `readlink` manpage: `only the target of the symbolic link is printed. If the given argument is not a symbolic link, readlink will print nothing and exit with an error`, so readlink won't work for this purpose on OSX – SnoringFrog Jan 14 '16 at 18:53
  • @Dalin These options can be found by installing the GNU coreutils. – GDP2 Sep 11 '16 at 08:15
  • Works fine on MacOS 10.3 (Mavericks) – Elise van Looij Mar 15 '18 at 10:02
  • I think that OP did not ask for resolving symlinks. If that is true, then this is a faulty answer. An "absolute path" and "resolved symlinks" are two different things. For getting absolute paths without resolving symlinks, `realpath -s` can be be used. – nagylzs Oct 11 '19 at 13:09
  • From `readlink` manpage (coreutils 8.32-1): "Note realpath(1) is the preferred command to use for canonicalization functionality". They are both from `coreutils`. – Oliver Gondža Sep 09 '21 at 10:27
  • i got ` -f: command not found` in gitlab ci, anyone can help me? – Ade Dyas Oct 08 '21 at 07:58
  • @SnoringFrog: I just tested `readlink -f` on the current macOS (12.6) and it works as intended. If the argument is a symlink, it will show the absolute path of the symlink target. – comfreak Oct 21 '22 at 14:02
  • @comfreak it works for me on macOS 12.6 too, but `man readlink` says that -f accepts format value. I'm confused – Basilevs Oct 31 '22 at 09:06
322
#! /bin/sh
echo "$(cd "$(dirname -- "$1")" >/dev/null; pwd -P)/$(basename -- "$1")"
doekman
  • 18,750
  • 20
  • 65
  • 86
dogbane
  • 266,786
  • 75
  • 396
  • 414
  • 8
    Don't forget to quote all the stuff. If you don't understand why, try your program on a file `a b` (two spaces between a and b, SO eats one of them). – Roman Cheplyaka Oct 12 '10 at 17:27
  • 1
    also try this on "/" it turns into "///" – George Jun 28 '12 at 22:12
  • 41
    the only POSIX solution so far that does not require you to write and compile an executable since readlink and realpath are not POSIX – Ciro Santilli OurBigBook.com Jun 29 '13 at 12:49
  • What @Roman Cheplyaka was getting at was this I think: $( cd "$( dirname "$1" )" && pwd ) – Dalin Nov 21 '13 at 21:35
  • 34
    I used `function realpath { echo $(cd $(dirname $1); pwd)/$(basename $1); }` to make myself a `realpath` command (currently accepted answer) on OS X. Thanks! – James Bedford Jun 20 '14 at 19:13
  • 1
    I used this method to define log filenames for scripts: `LOG=$(echo $(cd $(dirname $0); pwd)/$(basename $0 .sh).log)` – DrStrangepork Sep 12 '14 at 13:53
  • At least on OSX 10.10.3 this does not expand symbolic links to files. I don't understand how this is helpful. e.g. in `ln -s ~/src/myutil/myutil.sh ~/bin/; realpath ~/bin/myutil.sh` the command myutil.sh tries to find myutil.ini using realpath and cannot find it in ~/bin because it is in ~/src/myutil/ – Bruno Bronosky Jul 01 '15 at 12:36
  • 1
    @BrunoBronosky This answer fulfils the requirements of the question and is useful in some cases. It seems like you are interested in this question, which also includes expanding symbolic links (also known as canonicalization): http://unix.stackexchange.com/questions/24293/converting-relative-path-to-absolute-path – Robin Green Aug 05 '15 at 18:33
  • Nice answer! I expanded it a bit to handle such names as . .. / etc correctly and put as a separate answer. – Alek Mar 16 '16 at 19:03
  • @JamesBedford this is not a correct `realpath` implementation 'cuz it doesn't resolve symlinks. – ivan_pozdeev Nov 08 '18 at 13:51
  • 1
    You're right! A `-P` argument needs to be given to pwd in order to resolve the symlinks. I'll go ahead and edit the answer. Thanks! – James Bedford Nov 09 '18 at 14:51
  • 1
    pwd -P` doesn't work if `$1` is a symlink, because the `pwd -P` gets called on the parent directory of the symlink, and not the symlink itself. It only works when you `$1` is a subdirectory of a symlink. – dosentmatter Jul 02 '19 at 18:29
  • 1
    I think you can just special case symlinks instead: `if [ -h "$1" ]; then ls -ld "$1" | awk '{ print $11 }'; else echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"; fi`. It still has problems with paths that have no dirname, such as `/`, which outputs `///`. – dosentmatter Jul 02 '19 at 18:53
  • this doesn't work if you provide e.g. `../first/second`- it will fail with `cd: ../first: File or directory not existent` – Michele Peresano Mar 18 '23 at 12:43
  • After searching and trying things for hours, his is the correct answer – Jerrod Horton Jun 29 '23 at 19:43
122

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
}
peterh
  • 18,404
  • 12
  • 87
  • 115
  • Nice function, but apparently you didn't understand the question. I wanted something for interactive use, not scripts. – dhardy Jan 25 '14 at 17:19
  • 1
    @dhardy. Not sure I understand your comment. What is it in the solution proposed here that excludes use from an interactive environment? ... btw, just like all the other alternative answers presented on this page. – peterh Jan 26 '14 at 11:39
  • Well, nolan6000, I included a capable (if inelegant) script to do this in the question, which then asked if there was a standard Unix utility to do the same thing. I'm surprised by all the other answers too to be honest — realpath covers the original use-case and readlink some related ones, so yet another answer doesn't help. – dhardy Feb 04 '14 at 13:15
  • Neither `realpath` nor `realink` is 'a standard Unix utility' so if that's what you were after then you've gone with the wrong answer. – peterh Feb 04 '14 at 13:53
  • You've got me there. Still, `realpath` is what I was after when I asked the question, and it's standard enough to be available as a package on all Linux distributions I've used recently. – dhardy Feb 06 '14 at 15:42
  • 6
    I would like to add that `realpath` comes from coreutils package, which also contains tools such as `who`, `touch` or `cat`. It's quite standar set of GNU tools, so you can be quite sure that this is installed on almost any linux based machine.That said, you are right: there are 2 issues with it: i) you will likely miss it in default install on non GNU unix systems (like OpenBSD) ii) this tools was introduced in version 8.15 (so it's quite new, from 2012), so you will miss it on long term stable systems (like RHEL 6). – marbu Feb 06 '14 at 22:22
  • 1
    Well, it seems at least that Continuum Analytics (makers of the [Anaconda Python distribution](https://store.continuum.io/cshop/anaconda/)) liked this answer. It is implemented (with a reference linking back here) in their ["activate" script](https://github.com/conda/conda/blob/master/bin/activate), which is used to activate virtual environments created by the conda tool. So… good one! – wjv Mar 06 '14 at 11:39
  • 1
    This only works for directories (since there is no else) and cannot cope with ".." and ".". See my answer that should handle everything: http://stackoverflow.com/a/23002317/2709 – Alexander Klimetschek Apr 11 '14 at 02:13
  • @AlexanderKlimetschek. You are absolutely right. I wanted to keep the stub simple. Then others can expand on it according to their needs. Exactly as you've done. Nice!. – peterh Apr 11 '14 at 11:54
  • thanks, but personally I made it `echo -n` just to make it more clipboard friendly – slf Sep 30 '14 at 17:42
  • 4
    @marbu realpath is not present on Ubuntu 14.04, or on Debian Wheezy. It may become somewhat standard over time, but it's certainly not now. Also note that the OP didn't say anything about linux or GNU. Bash is more widely used than that. – mc0e Nov 27 '14 at 11:45
  • At the cost of being larger, this would be much better if it gave errors when appropriate, including when acting on an empty string as input. – mc0e Nov 27 '14 at 11:47
  • how to handle symbolic link – Gary Gauh Mar 15 '18 at 05:38
  • Note that this supports reading the absolute path of a symlink, *rather than what the symplink points to*, which using `realpath` does not. – JL Peyret May 12 '22 at 20:52
81
$ readlink -m FILE
/path/to/FILE

This is better than readlink -e FILE or realpath, because it works even if the file doesn't exist.

Flimm
  • 136,138
  • 45
  • 251
  • 267
78

This relative path to absolute path converter shell function

  • requires no utilities (just cd and pwd)
  • works for directories and files
  • handles .. and .
  • handles spaces in dir or filenames
  • requires that file or directory exists
  • returns nothing if nothing exists at the given path
  • handles absolute paths as input (passes them through essentially)

Code:

function abspath() {
    # generate absolute path from relative path
    # $1     : relative filename
    # return : absolute path
    if [ -d "$1" ]; then
        # dir
        (cd "$1"; pwd)
    elif [ -f "$1" ]; then
        # file
        if [[ $1 = /* ]]; then
            echo "$1"
        elif [[ $1 == */* ]]; then
            echo "$(cd "${1%/*}"; pwd)/${1##*/}"
        else
            echo "$(pwd)/$1"
        fi
    fi
}

Sample:

# assume inside /parent/cur
abspath file.txt        => /parent/cur/file.txt
abspath .               => /parent/cur
abspath ..              => /parent
abspath ../dir/file.txt => /parent/dir/file.txt
abspath ../dir/../dir   => /parent/dir          # anything cd can handle
abspath doesnotexist    =>                      # empty result if file/dir does not exist
abspath /file.txt       => /file.txt            # handle absolute path input

Note: This is based on the answers from nolan6000 and bsingh, but fixes the file case.

I also understand that the original question was about an existing command line utility. But since this seems to be THE question on stackoverflow for that including shell scripts that want to have minimal dependencies, I put this script solution here, so I can find it later :)

Alexander Klimetschek
  • 3,585
  • 31
  • 36
  • Thanks. That's what I go with on OSX – Greg Nov 13 '14 at 04:47
  • This doesn't work for directories with spaces, i.e.: `$ abspath 'a b c/file.txt'` That's because the argument to `cd` isn't quoted. To overcome this, instead of quoting the entire argument to `echo` (is there a reason for these quotes?) quote only the path arguments. I.e. replace `echo "$(cd ${1%/*}; pwd)/${1##*/}"` with `echo $(cd "${1%/*}"; pwd)/${1##*/}`. – george Dec 24 '14 at 11:52
  • 1
    I looked through dozens of half-cocked solutions and this definitely appears to be the most concise and accurate bash-only solution. You could slim it even more by replacing `echo "$(cd "$1"; pwd)"` with `(cd "$1"; pwd)`. There is no need for echo here. – Six Mar 26 '15 at 14:07
  • @Six True, updated. The braces `( ... )` are still needed, so the `cd` happens in a subshell, as we don't want to change the working directory for any callers of `abspath`. – Alexander Klimetschek Mar 27 '15 at 01:37
  • This is a well thought out answer, but it doesn't work for symbolic links to files, only to directories. So, if you have a script that looks for a config file in the same directory as the script, you can't symlink the script to ~/bin. This is a pretty common practice for tools that are under version control. – Bruno Bronosky Jul 01 '15 at 12:39
  • @BrunoBronosky it works fine for me with symlinks. Do you have an example that does not work? Note that it will return the absolute path (in case of symlinks based on the symlink you pass in), not the "canonical" path, which I would consider having a separate tool/function for that is likely much more difficult to implement in a simple shell function. – Alexander Klimetschek Jul 01 '15 at 19:28
  • I was looking for something like readlink which gives the realpath with the symlink expanded. Upon closer examination, this is not the right question for that. – Bruno Bronosky Jul 01 '15 at 20:27
  • Alexander, thanks very much! I edited to `if [[ $1 = /* ]] ; then echo "$1" ; elif [[ $1 = */* ...` in the `file` branch so it could also handle absolute paths as input. – cxw Oct 20 '17 at 15:44
  • @cxw Thanks, good find. This was only a problem with single-level absolute file paths such as `/file.txt`, while `/dir` or `/dir/file.txt` worked before. I fixed it by incorporating your change. – Alexander Klimetschek Oct 20 '17 at 20:51
  • Really nice solution. One possible drawback: it requires that the path $1 exists, at least the directory parts of it. – leamas Mar 03 '20 at 11:34
  • This is a good efficient solution, and probably works with dash as well as bash, but will fail on bourne. It might be worth mentioning that this expects posix shell pattern expansion, which will be available pretty much anywhere on Linux but may fail on aging commercial unix systems. – jrodman Mar 03 '23 at 20:59
28

The find command may help

find $PWD -name ex*
find $PWD -name example.log

Lists all the files in or below the current directory with names matching the pattern. You can simplify it if you will only get a few results (e.g. directory near bottom of tree containing few files), just

find $PWD

I use this on Solaris 10, which doesn't have the other utilities mentioned.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
lessthanideal
  • 1,084
  • 13
  • 14
  • 1
    i did not grok this comment on the first read; the key point here is that if you give an absolute path to the find command then it will output results in absolute path. so using $PWD is the absolute path of where you are so you get the output as absolute. – simbo1905 Jan 01 '13 at 19:05
  • 4
    If you rely on `PWD`, you could simply use `$PWD/$filename`. – jonny Apr 24 '14 at 19:05
  • This approach can be improved with use of -maxdepth 1. Also don't forget about quoting - potential security issues here, depending how you use this. – mc0e Nov 27 '14 at 11:57
  • 1
    @jonny: $PWD/$filename fails if $filename is already absolute. – mc0e Nov 27 '14 at 11:58
  • This fails if the file name is given as "./filename.txt" (with leading dot and slash). – Mark Stosberg Jul 29 '15 at 18:15
  • Fails if there are multiple matches for the filename below $PWD. Can be painfully slow if there are many directories. – user48956 Jan 21 '16 at 02:43
21

Here's a zsh-only function that I like for its compactness. It uses the ‘A’ expansion modifier — see zshexpn(1).

realpath() { for f in "$@"; do echo ${f}(:A); done }
wjv
  • 2,288
  • 1
  • 18
  • 21
17

If you don't have readlink or realpath utilities than you can use following function which works in bash and zsh (not sure about the rest).

abspath () { case "$1" in /*)printf "%s\n" "$1";; *)printf "%s\n" "$PWD/$1";; esac; }

This also works for nonexistent files (as does the python function os.path.abspath).

Unfortunately abspath ./../somefile doesn't get rid of the dots.

hluk
  • 5,848
  • 2
  • 21
  • 19
  • 1
    Looks portable to me. On the other hand, will break for example on a filename containing a newline. – Roman Cheplyaka Oct 12 '10 at 17:31
  • 1
    To improve further, replace `echo "$1"` with `printf "%s\n" "$1"` (same with the second echo). `echo` may interpret backslashes inside its arguments. – Roman Cheplyaka Oct 12 '10 at 17:52
  • Again, you're right! Really, the behavior of echo command in zsh is different from bash. – hluk Oct 12 '10 at 18:29
  • 1
    Strictly speaking, under POSIX behaviour of echo is undefined if arguments contain backslashes. However, it is defined under XSI extension (namely, echo should interpret the escape sequences). But both bash and zsh are soo far even from POSIX compliance... – Roman Cheplyaka Oct 12 '10 at 19:58
  • Sorry, but I don't see the point. I already provided an answer as a script with more features that this and it doesn't need to be used within a shell script. – dhardy Oct 13 '10 at 15:43
9

There is generally no such thing as the absolute path to a file (this statement means that there may be more than one in general, hence the use of the definite article the is not appropriate). An absolute path is any path that start from the root "/" and designates a file without ambiguity independently of the working directory.(see for example wikipedia).

A relative path is a path that is to be interpreted starting from another directory. It may be the working directory if it is a relative path being manipulated by an application (though not necessarily). When it is in a symbolic link in a directory, it is generally intended to be relative to that directory (though the user may have other uses in mind).

Hence an absolute path is just a path relative to the root directory.

A path (absolute or relative) may or may not contain symbolic links. If it does not, it is also somewhat impervious to changes in the linking structure, but this is not necessarily required or even desirable. Some people call canonical path ( or canonical file name or resolved path) an absolute path in which all symbolic links have been resolved, i.e. have been replaced by a path to whetever they link to. The commands realpath and readlink both look for a canonical path, but only realpath has an option for getting an absolute path without bothering to resolve symbolic links (along with several other options to get various kind of paths, absolute or relative to some directory).

This calls for several remarks:

  1. symbolic links can only be resolved if whatever they are supposed to link to is already created, which is obviously not always the case. The commands realpath and readlink have options to account for that.
  2. a directory on a path can later become a symbolic link, which means that the path is no longer canonical. Hence the concept is time (or environment) dependent.
  3. even in the ideal case, when all symbolic links can be resolved, there may still be more than one canonical path to a file, for two reasons:
    • the partition containing the file may have been mounted simultaneously (ro) on several mount points.
    • there may be hard links to the file, meaning essentially the the file exists in several different directories.

Hence, even with the much more restrictive definition of canonical path, there may be several canonical paths to a file. This also means that the qualifier canonical is somewhat inadequate since it usually implies a notion of uniqueness.

This expands a brief discussion of the topic in an answer to another similar question at Bash: retrieve absolute path given relative

My conclusion is that realpath is better designed and much more flexible than readlink. The only use of readlink that is not covered by realpath is the call without option returning the value of a symbolic link.

Community
  • 1
  • 1
babou
  • 632
  • 8
  • 10
  • I do not mind being downvoted, but I like to understand why so that I can learn something, either technical or regarding what is considered proper practice on the site. Well ... at least it induced me to register with [Meta Stack Overflow](http://meta.stackoverflow.com) to better understand local sociology. I do recommend it. -- Still, if anyone has an opinion about the inappropriateness of my answer, I am interested. – babou May 20 '13 at 15:35
  • 1
    (a) the question has a valid answer from 2+1/2 years ago, (b) there is a (single) absolute path corresponding to a *relative path* (which is what I wanted), though maybe not to a file as you pointed out. BTW any comments about `realpath` vs `readlink` or even about symbolic links are surplus to what I asked. – dhardy May 28 '13 at 18:56
  • 1
    Second comment: short answers are very often more useful than long lectures. Stack overflow is usually used to ask specific questions. – dhardy May 28 '13 at 18:58
  • @dhardy Thank you for taking the time for a detailed answer. I find it really helpful to understand communication intercourse on this site, and to clarify my own thoughts on this topic. Regarding the specific issues you raise, please allow me to reply to them in more detail. – babou May 29 '13 at 11:57
  • 5
    1- the fact that a question has had a valid answer for however long does not mean that it is closed. The questions are not intended for personnal use only. Indeed, there are badges for replies that collect more votes than the validated answer. And with new tools, the "best" answer can change over time. Many people use old answers accessed through web search. – babou May 29 '13 at 11:58
  • 1
    2 - You insist that *there is a (single) absolute path corresponding to a relative path*. While I do guess what you mean by "corresponding", i.e. a path derived from the original through a given set of transformations, your statement is still incorrect. There is a unique "corresponding" canonical path. The word `canonical` refers precisely to this uniqueness relative to a set of transformations. But, for example, /dev/cdrom is a perfectly good absolute path, even though it is actually a link to /dev/sr0. Both point to the same device. My original answer pointed to a relevant web article. – babou May 29 '13 at 11:58
  • 3 - Since there are many solutions suggested as answers, it may be useful to readers to get a comparative assessment of the answers, to get to understand the why as well as to know the how. I happen to agree with you on the answer, but that is immaterial. It took me some time (much more than you would think) to be comfortable with that assessment, and I try to spare that work to others. Sometimes I edit my own questions to explain, afterwards, why I chose a given answer, so as to help future readers. Actually there seems to be a bit of controversy regarding `realpath` and `readlink`. – babou May 29 '13 at 11:59
  • 4 - `Short answers are very often more useful than long lectures`. You are absolutely right. If you go back to my [original answer](http://stackoverflow.com/posts/16637542/revisions) you will see that it was only 8 lines. Being downvoted without explanation, I assumed I had to be more precise, though I did not even know whether the problem was technical or linguistic. I started working on this text for my own sake, leaving it there because it was convenient and because I was still hoping to get reactions. But the same question is asked in too many places. – babou May 29 '13 at 12:00
  • I fail to see what this answer is preaching. The algorithm of absolutizing a path is quite clear and unambiguous: `absolute_path = path if is_absolute(path) else current_dir + sep + path`. Who cares that many absolute paths are possible if the FS supports symlinks? This question does not ask how to compare them! – ivan_pozdeev Nov 08 '18 at 14:00
  • @ivan_pozdeev Busy week. Could not answer earlier (stuff is 5 years old). The question was asking for a single command. Your answer is composite. The accepted answer suggested realpath which is a single command. Another is readlink. But no one explains what they do, and the result may be adequate, or not, because there is no unicity of absolute path (while unicity is implied in the question), and they do not always behave identically, depending on later use. Your answer may not be right depending on context, on intended use of result, which is a piece of text to be interpreted later. – babou Nov 11 '18 at 13:43
  • So this is a remark on other answers and does not give any answers itself. That's what was rubbing me the wrong way: these are off topic at SO. – ivan_pozdeev Nov 11 '18 at 16:05
  • @ivan_pozdeev In no way a remark on other answers. First, the question is bad. That is my first remark on my original (short) answer. I made it longer as users missed the point which is obviously more subtle than expected. Answers are given, but incomplete regarding use. One can say the OP did not ask, but the phrasing of his question made it clear he was missing a point. I was not commenting on other questions, but giving an answer complete enough to take into account whatever hidden but essential information may be at stakes, unbeknownst to OP. This does agree with SO policies. – babou Nov 11 '18 at 17:14
  • @ivan_pozdeev May I point out that your first comment makes the same mistake as the OP. What you describe is one way to get an absolute path. It is not the only one, and they do not all give the same result. And you call it **the** algorithm, thereby implying that it is unique, or at least that they all give equivalent result, which is wrong. What right do you have to present your answer a **the** unique one, especially if you know there are others, but do not know which is more appropriate. – babou Nov 11 '18 at 17:42
  • All I implied is that the algorithm I gave is the "default" one, the one implied when "absolute path" is mentioned without any further qualifications. The OP didn't ask for "all possible absolute paths" so unless they say otherwise, this is implied. Judging by the OP's actions though, they were fine with any of the possible answers. – ivan_pozdeev Nov 11 '18 at 20:56
  • @ivan_pozdeev That was 5 years ago and I do not remember the exact path of my thinking. I had read all the answers, and was aware of possible traps. If the intent of the OP should be followed the way you state, then all the answers suggesting readlink are wrong (do not forget downvoting them :). One had more than 100 upvote when I answered. Though symlinks may be expanded, I was aware that it is often undesirable (as you imply too), but I tried to address the isssue constructively since it could help some users. Explaining why one choice may be better than the other is also an answer. – babou Nov 11 '18 at 22:52
7

The simplest if you want to use only builtins is probably:

find `pwd` -name fileName

Only an extra two words to type, and this will work on all unix systems, as well as OSX.

Arj
  • 1,981
  • 4
  • 25
  • 45
  • I would add `-maxdepth 1` before `-name` (supposing the file is in the current directory). Without that it will work with files in any subdirectory of pwd, but not with others. – Walter Tross May 04 '20 at 15:01
  • Indeed you are right, the question did specify that the file would be in the current directory. What do you mean "but not with others"? – Arj May 06 '20 at 08:47
  • I mean `find` won't find a file outside the directory tree underneath `pwd`. E.g., if you try to `find . -name ../existingFile`, it fails. – Walter Tross May 06 '20 at 13:40
  • Fair enough, yes this'd assume your'e wanting to print the full path of a file in your cwd (as per original question), or anywhere in your cwd tree (not in original question). – Arj May 12 '20 at 15:29
6

The dogbane answer with the description what is coming on:

#! /bin/sh
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"

Explanation:

  1. This script get relative path as argument "$1"
  2. Then we get dirname part of that path (you can pass either dir or file to this script): dirname "$1"
  3. Then we cd "$(dirname "$1") into this relative dir and get absolute path for it by running pwd shell command
  4. After that we append basename to absolute path: $(basename "$1")
  5. As final step we echo it
Community
  • 1
  • 1
Eugen Konkov
  • 22,193
  • 17
  • 108
  • 158
6

The top answers in this question may be misleading in some cases. Imagine that the file, whose absolute path you want to find, is in the $PATH variable:

# node is in $PATH variable
type -P node
# /home/user/.asdf/shims/node
cd /tmp
touch node  # But because there is a file with the same name inside the current dir check out what happens below
readlink -e node
# /tmp/node
readlink -m node
# /tmp/node
readlink -f node
# /tmp/node
echo "$(cd "$(dirname "node")"; pwd -P)/$(basename "node")"
# /tmp/node
realpath node
# /tmp/node
realpath -e node
# /tmp/node

# Now let's say that for some reason node does not exist in current directory
rm node
readlink -e node
# <nothing printed>
readlink -m node    
# /tmp/node         # Note: /tmp/node does not exist, but is printed
readlink -f node
# /tmp/node         # Note: /tmp/node does not exist, but is printed
echo "$(cd "$(dirname "node")"; pwd -P)/$(basename "node")"
# /tmp/node         # Note: /tmp/node does not exist, but is printed
realpath node
# /tmp/node         # Note: /tmp/node does not exist, but is printed
realpath -e node
# realpath: node: No such file or directory

Based on the above I can conclude that: realpath -e and readlink -e can be used for finding the absolute path of a file, that we expect to exist in current directory, without result being affected by the $PATH variable. The only difference is that realpath outputs to stderr, but both will return error code if file is not found:

cd /tmp
rm node
realpath -e node ; echo $?
# realpath: node: No such file or directory
# 1
readlink -e node ; echo $?
# 1

Now in case you want the absolute path a of a file that exists in $PATH, the following command would be suitable, independently on whether a file with same name exists in current dir.

type -P example.txt
# /path/to/example.txt

# Or if you want to follow links
readlink -e $(type -P example.txt)
# /originalpath/to/example.txt

# If the file you are looking for is an executable (and wrap again through `readlink -e` for following links )
which executablefile
# /opt/bin/executablefile

And a, fallback to $PATH if missing, example:

cd /tmp
touch node
echo $(readlink -e node || type -P node)
# /tmp/node
rm node
echo $(readlink -e node || type -P node)
# /home/user/.asdf/shims/node
Marinos An
  • 9,481
  • 6
  • 63
  • 96
5

Answer with Homebrew

realpath is the best answer, but if you don't have it installed, you must first run brew install coreutils which will install coreutils with lots of awesome functions. Writing a custom function and exporting it is too much work and risk for error for something like this, here are two lines:

$ brew install coreutils
$ realpath your-file-name.json 
Zorayr
  • 23,770
  • 8
  • 136
  • 129
  • realpath resolves symlinks – balupton Sep 26 '21 at 18:21
  • @balupton in newer versions of realpath, one can use `realpath --no-symlinks ` if they want the full path to a file **without** resolving symlinks (as you pointed out, they are resolved by default). I'm on Fedora 35 right now and confirm GNU realpath v8.32 supports this. Might be in older versions too; I did not test on any Debian-based distros / research when the option was added. Also did not test BSD versions so no clue for Mac / OpenBSD / etc. So in terms of Mac/homebrew, you would need the gnu coretools for this. – zpangwin Nov 14 '22 at 21:48
2

For directories dirname gets tripped for ../ and returns ./.

nolan6000's function can be modified to fix that:

get_abs_filename() {
  # $1 : relative filename
  if [ -d "${1%/*}" ]; then
    echo "$(cd ${1%/*}; pwd)/${1##*/}"
  fi
}
Community
  • 1
  • 1
bsingh
  • 37
  • 2
  • 1
    Welcome to SO! For small changes and remarks, adding a comment to an existing answer might be more appropriate than adding an answer, especially if this copied answer lacks a lot of the other information of the original answer. Once you've collected a bit more reputation, you are able to add comments to other people's answers. For now, try to collect reputation by asking unique, valuable questions or providing stand alone, good answers. – cfi Mar 21 '14 at 08:16
  • 1
    Since you are referring to nolan6000's answer, please note that the poster dhardy already commented that he would not accept nolan6000's answer because he is not looking for a script. So strictly speaking your answer does not answer the question. – cfi Mar 21 '14 at 08:17
  • the function can be added to `.profile` and hence available for interactive use. – adib Mar 19 '15 at 06:19
  • This won't work if `$1` is a plain filename: `get_abs_filename foo` -> nothing (or `/foo/foo` if `foo` is a directory). – ivan_pozdeev Nov 08 '18 at 14:05
2

This is not an answer to the question, but for those who does scripting:

echo `cd "$1" 2>/dev/null&&pwd||(cd "$(dirname "$1")";pwd|sed "s|/*\$|/${1##*/}|")`

it handles / .. ./ etc correctly. I also seems to work on OSX

Alek
  • 634
  • 7
  • 7
2

I have placed the following script on my system & I call it as a bash alias for when I want to quickly grab the full path to a file in the current dir:

#!/bin/bash
/usr/bin/find "$PWD" -maxdepth 1 -mindepth 1 -name "$1"

I am not sure why, but, on OS X when called by a script "$PWD" expands to the absolute path. When the find command is called on the command line, it doesn't. But it does what I want... enjoy.

  • `$PWD` always has the working directory's absolute path. Also, `find` won't tolerate slashes, so you're not answering the question as asked. A quicker way to do what you're looking to do would simply be `echo "$PWD/$1"` – Adam Katz Dec 20 '18 at 22:46
1
#! /bin/bash

file="$@"
realpath "$file" 2>/dev/null || eval realpath $(echo $file | sed 's/ /\\ /g')

This makes up for the shortcomings of realpath, store it in a shell script fullpath. You can now call:

$ cd && touch a\ a && rm A 2>/dev/null 
$ fullpath "a a"
/home/user/a a
$ fullpath ~/a\ a
/home/user/a a
$ fullpath A
A: No such file or directory.
ShellFish
  • 4,351
  • 1
  • 20
  • 33
1

An alternative to get the absolute path in Ruby:

realpath() {ruby -e "require 'Pathname'; puts Pathname.new('$1').realpath.to_s";}

Works with no arguments (current folder) and relative and absolute file or folder path as agument.

Atika
  • 1,560
  • 18
  • 18
1

The answer of Alexander Klimetschek is okay if your script may insist on a bash or bash compatible shell being present. It won't work with a shell that is only POSIX conforming.

Also when the final file is a file in root, the output will be //file, which is not technically incorrect (double / are treated like single ones by the system) but it looks strange.

Here's a version that works with every POSIX conforming shell, all external tools it is using are also required by the POSIX standard, and it explicitly handles the root-file case:

#!/bin/sh

abspath ( ) {
    if [ ! -e "$1" ]; then
        return 1
    fi

    file=""
    dir="$1"
    if [ ! -d "$dir" ]; then
        file=$(basename "$dir")
        dir=$(dirname "$dir")
    fi

    case "$dir" in
        /*) ;;
        *) dir="$(pwd)/$dir"
    esac
    result=$(cd "$dir" && pwd)

    if [ -n "$file" ]; then
        case "$result" in
            */) ;;
             *) result="$result/"
        esac
        result="$result$file"
    fi

    printf "%s\n" "$result"
}

abspath "$1"

Put that into a file and make it executable and you have a CLI tool to quickly get the absolute path of files and directories. Or just copy the function and use it in your own POSIX conforming scripts. It turns relative paths into absolute ones and returns absolute ones as is.

Interesting modifications:

If you replace the line result=$(cd "$dir" && pwd) with result=$(cd "$dir" && pwd -P), then all symbolic links in the path to the final file are resolved as well.

If you are not interested into the first modification, you can optimize the absolute case by returning early:

abspath ( ) {
    if [ ! -e "$1" ]; then
        return 1
    fi

    case "$1" in
        /*)
            printf "%s\n" "$1"
            return 0
    esac

    file=""
    dir="$1"
    if [ ! -d "$dir" ]; then
        file=$(basename "$dir")
        dir=$(dirname "$dir")
    fi

    result=$(cd "$dir" && pwd)

    if [ -n "$file" ]; then
        case "$result" in
            */) ;;
            *) result="$result/"
        esac
        result="$result$file"
    fi

    printf "%s\n" "$result"
}

And since the question will arise: Why printf instead of echo?

echo is intended primary to print messages for the user to stdout. A lot of echo behavior that script writers rely on is in fact unspecified. Not even the famous -n is standardized or the usage of \t for tab. The POSIX standard says:

A string to be written to standard output. If the first operand is -n, or if any of the operands contain a character, the results are implementation-defined.
- https://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html

Thus whenever you want to write something to stdout and it's not for the purpose of printing a message to the user, the recommendation is to use printf as the behavior of printf is exactly defined. My function uses stdout to pass out a result, this is not a message for the user and thus only using printf guarantees perfect portability.

Mecki
  • 125,244
  • 33
  • 244
  • 253
0

I use the single line

(cd ${FILENAME%/*}; pwd)

However, this can only be used when $FILENAME has a leading path of any kind (relative or absolute) that actually exists. If there is no leading path at all, then the answer is simply $PWD. If the leading path does not exist, then the answer may be indeterminate, otherwise and the answer is simply ${FILENAME%/*} if the path is absolute.

Putting this all together I would suggest using the following function

function abspath() {
  # argument 1: file pathname (relative or absolute)
  # returns: file pathname (absolute)
  if [ "$1" == "${1##*/}" ]; then # no path at all
    echo "$PWD"
  elif [ "${1:0:1}" == "/" -a "${1/../}" == "$1" ]; then # strictly absolute path
    echo "${1%/*}"
  else # relative path (may fail if a needed folder is non-existent)
    echo "$(cd ${1%/*}; pwd)"
  fi
}

Note also that this only work in bash and compatible shells. I don't believe the substitutions work in the simple shell sh.

0

In zsh, yes there is.

Assuming your path is stored in a variable, you can use the :P modifier to get the absolute path of the file.

Example:

f='../example.txt'
echo ${f:P}

The above will print the absolute path of ../example.txt.

You can alternatively use the :A modifier to further resolve symbolic links, but this depends on realpath being installed.

f='../example.txt'
echo ${f:A}

See https://zsh.sourceforge.io/Doc/Release/Expansion.html#Modifiers

smac89
  • 39,374
  • 15
  • 132
  • 179
-1

Hey guys I know it's an old thread but I am just posting this for reference to anybody else who visited this like me. If i understood the question correctly, I think the locate $filename command. It displays the absolute path of the file supplied, but only if it exists.

icyyd
  • 1
  • 2
  • 5
    `locate` is a utility to search for files/paths by name. It may do the job but may also find other matches and may not find an existing file without calling `updatedb` as root first. – dhardy Dec 05 '14 at 18:17