How to obtain the absolute path of a file via Shell (BASH/ZSH/SH)?
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 # 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)
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.
22 Answers 22
$ realpath example.txt /home/username/example.txt
+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.
Try readlink which will resolve symbolic links:
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.
@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 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
#! /bin/sh echo "$(cd "$(dirname -- "$1")" >/dev/null; pwd -P)/$(basename -- "$1")"
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).
the only POSIX solution so far that does not require you to write and compile an executable since readlink and realpath are not POSIX
I used function realpath < echo $(cd $(dirname $1); pwd)/$(basename $1); >to make myself a realpath command (currently accepted answer) on OS X. Thanks!
I used this method to define log filenames for scripts: LOG=$(echo $(cd $(dirname $0); pwd)/$(basename $0 .sh).log)
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:
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:
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:
If you want to resolve the ‘..’ you will need to make the script like:
get_abs_filename() < # $1 : relative filename filename=$1 parentdir=$(dirname "$") if [ -d "$" ]; then echo "$(cd "$" && pwd)" elif [ -d "$" ]; then echo "$(cd "$" && pwd)/$(basename "$")" fi >
@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.
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).
Well, it seems at least that Continuum Analytics (makers of the Anaconda Python distribution) liked this answer. It is implemented (with a reference linking back here) in their «activate» script, which is used to activate virtual environments created by the conda tool. So… good one!
This only works for directories (since there is no else) and cannot cope with «..» and «.». See my answer that should handle everything: stackoverflow.com/a/23002317/2709
@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.
$ 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.
I do have readlink available on OS X 10.9 (Mavericks), so this this is definitely the best answer here.
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)
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 "$"; pwd)/$" else echo "$(pwd)/$1" fi fi >
# 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 🙂
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)/$» with echo $(cd «$<1%/*>«; pwd)/$ .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 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 .
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.
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
I use this on Solaris 10, which doesn’t have the other utilities mentioned.
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.
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.
Here’s a zsh-only function that I like for its compactness. It uses the ‘A’ expansion modifier — see zshexpn(1).
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).
This also works for nonexistent files (as does the python function os.path.abspath ).
Unfortunately abspath ./../somefile doesn’t get rid of the dots.
To improve further, replace echo «$1» with printf «%s\n» «$1» (same with the second echo). echo may interpret backslashes inside its arguments.
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.
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.
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:
- 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.
- 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.
- 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.