Linux bash current path

Reliable way for a Bash script to get the full path to itself [duplicate]

So, various ways of doing it, but they all have their caveats.

What would be a better way? Where «better» means:

  • Gives me the absolute path.
  • Takes out funky bits even when invoked in a convoluted way (see comment on #2 above). (E.g., at least moderately canonicalizes the path.)
  • Relies only on Bash-isms or things that are almost certain to be on most popular flavors of *nix systems (GNU/Linux, BSD and BSD-like systems like OS X, etc.).
  • Avoids calling external programs if possible (e.g., prefers Bash built-ins).
  • (Updated, thanks for the heads up, wich) It doesn’t have to resolve symlinks (in fact, I’d kind of prefer it left them alone, but that’s not a requirement).

(1.) the link you give in your own question has about 10x question-upvotes, 10x favorites, >15x answer-upvotes. (2.) Your summary is somewhat disingenious. (The link you gave has a first revision answer of «DIRECTORY=$(cd dirname $0 && pwd)» . which does not match your summary «getting the path of the script via dirname $0″and does not as you say «return a relative path».)

This is not exactly a duplicate of stackoverflow.com/questions/59895/… . That question is asking for source directory, which could also include relative path (as evidenced by some of the answers suggesting $0 ). This question is specifically absolute path, which is different.

23 Answers 23

Here’s what I’ve come up with (edit: plus some tweaks provided by sfstewman, levigroker, Kyle Strand, and Rob Kennedy), that seems to mostly fit my «better» criteria:

SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" 

That SCRIPTPATH line seems particularly roundabout, but we need it rather than SCRIPTPATH=`pwd` in order to properly handle spaces and symlinks.

The inclusion of output redirection ( >/dev/null 2>&1 ) handles the rare(?) case where cd might produce output that would interfere with the surrounding $( . ) capture. (Such as cd being overridden to also ls a directory after switching to it.)

Note also that esoteric situations, such as executing a script that isn’t coming from a file in an accessible file system at all (which is perfectly possible), is not catered to there (or in any of the other answers I’ve seen).

The — after cd and before «$0» are in case the directory starts with a — .

This does not work if the script is in a directory from the $PATH and you call it bash scriptname . In such a case $0 does not contain any path, just scriptname .

-1 (please don’t take it personnaly, it is just so that the real answer cat maybe get closer to the top): I used to do a similar thing (I used: «$(cd -P «$(dirname «$0″)» && pwd)» until today, but Andrew Norrie’s answer covers more cases (ie : PATH=»/some/path:$PATH» ; bash «script_in_path» : will only work with his answer, not with yours (as $0 contains only «script_in_path» and no indication of where (in $PATH) bash found it). correct is : ABSOLUTE_PATH=»$(cd «$(dirname «$»)» && pwd)/$(basename «$»)» (ie, @andrew-norrie ‘s answer: covers all cases, imo)

Читайте также:  Simple driver in linux

I would prefer if you used dirname «$» rather than dirname «$0» for added support for sourced scripts.

I’m surprised that the realpath command hasn’t been mentioned here. My understanding is that it is widely portable / ported.

Your initial solution becomes:

SCRIPT=$(realpath "$0") SCRIPTPATH=$(dirname "$SCRIPT") 

And to leave symbolic links unresolved per your preference:

SCRIPT=$(realpath -s "$0") SCRIPTPATH=$(dirname "$SCRIPT") 

@bolinfest: realpath is part of GNU coreutils since Jan 2012 / v8.15. If you’re running a recent Linux distro that includes coreutils (which should be basically all of them) and realpath is missing, the packagers have certainly gone out of their way to separate realpath into another package. (current coreutils version is 8.21, released 2013-02-04. The version I am running (on Arch Linux) appears to be this one, 8.21, and the package includes realpath , as any right-thinking package would 😉

It is also an extra package on Debian Wheezy. The coreutils version is 8.13 and does not include it yet.

Many if not most of these answers are buggy when given filenames with whitespace. SCRIPT_PATH=$(dirname «$(realpath -s «$0″)») is a fixed version of GuySoft’s answer, though using $BASH_SOURCE would be more reliable still than $0 (and this depends on having realpath , a non-POSIX tool, installed).

The simplest way that I have found to get a full canonical path in Bash is to use cd and pwd :

ABSOLUTE_PATH="$(cd "$(dirname "$")" && pwd)/$(basename "$")" 

Using $ instead of $0 produces the same behavior regardless of whether the script is invoked as or source .

This doesn’t seem to work if the originally-specified file is a symlink. I think you need to use something like readlink (or ls) in a loop, to make sure you’ve found a final non-symlink file. I’ve been on the lookout for something more concise, but in any case you can find the last version of the solution I used in the Android codebase, under dalvik/dx/etc/dx .

@over_optimistic I’m not convinced that -P helps here: If $0 names a symlink, then cd $(dirname $0); pwd -P still just tells you what directory the symlink is in and not the physical directory where the actual script resides. You really need to use something like readlink on the script name, except that readlink is not actually POSIX and it does seem to vary in practice between OSes.

@danfuzz: linux.die.net/man/8/symlinks looks like a good thing to use, to both have «cleaner» symlinks, and find their full path equivalent

+1 works nicely in all reasonable scenarios on a mac. No external dependencies and executes in 1 line. I use it to get the script’s directory like so: SCRIPTPATH=$(cd `dirname «$»` && pwd)

There’s more variants at the linked answer, e.g. for the case where the script itself is a symlink.

I just found out that the above does not work when the script is straight out executed rather than sourced: stackoverflow.com/questions/27880087/…

Get the absolute path of a shell script

It does not use the -f option in readlink, and it should therefore work on BSD/Mac OS X.

Читайте также:  Удаленное управление linux putty

Supports

  • source ./script (When called by the . dot operator)
  • Absolute path /path/to/script
  • Relative path like ./script
  • /path/dir1/../dir2/dir3/../script
  • When called from symlink
  • When symlink is nested eg) foo->dir1/dir2/bar bar->./../doe doe->script
  • When caller changes the scripts name

I am looking for corner cases where this code does not work. Please let me know.

Code

pushd . > /dev/null SCRIPT_PATH="$"; while([ -h "$" ]); do cd "`dirname "$"`" SCRIPT_PATH="$(readlink "`basename "$"`")"; done cd "`dirname "$"`" > /dev/null SCRIPT_PATH="`pwd`"; popd > /dev/null echo "srcipt=[$]" echo "pwd =[`pwd`]" 

Known issus

The script must be on disk somewhere. Let it be over a network. If you try to run this script from a PIPE it will not work

wget -o /dev/null -O - http://host.domain/dir/script.sh |bash 

Technically speaking, it is undefined. Practically speaking, there is no sane way to detect this. (A co-process can not access the environment of the parent.)

As stated elsewhere, and it’s not quite an edge case I think, but readlink -f is not a standard parameter and very well not be available, e.g. on my BSD.

I ran into a corner case where this didn’t work. I had a script under ~/scripts/myscript and a symlink at ~/bin/myscript which pointed to ../scripts/myscript . Running ~/bin/myscript from ~/ caused it to think the script’s location was ~/ . The solution here worked fine, which looks pretty similar to your solution

SCRIPT_PATH=$(dirname `which $0`) 

which prints to standard output the full path of the executable that would have been executed when the passed argument had been entered at the shell prompt (which is what $0 contains)

dirname strips the non-directory suffix from a file name.

Hence you end up with the full path of the script, no matter if the path was specified or not.

@BobbyNorton Yes because the non-directory suffix at that point is simply . . However, if you run which on the script name and store it in a variable, such as a=$(which ./myscript) , it will return the full path, such as /tmp/myscript , which if passed to dirname will return the path. Interestingly if you run which ./myscript and not assign it to a variable, it simply returns ./myscript . I suspect this is because when assigning it to a variable it executes in another shell and passes the complete path to bash at that time.

@Matt I would not use the ‘which’ command unless the command is in the current path. Which only searches for the command in the path. If your script is not in the path it will never be found.

If this script is sourced, $0 will refer to the ‘sourcing’ script, and may not give you what you want

As realpath is not installed per default on my Linux system, the following works for me:

SCRIPT="$(readlink --canonicalize-existing "$0")" SCRIPTPATH="$(dirname "$SCRIPT")" 

$SCRIPT will contain the real file path to the script and $SCRIPTPATH the real path of the directory containing the script.

Before using this read the comments of this answer.

The OP already noted this solution and disregarded it for not being POSIX — but this is nice and tidy for GNU-based systems at least. The key feature of this is that it resolves symbolic links.

Читайте также:  Изменить название группы linux

Easy to read? Below is an alternative. It ignores symlinks

#!/bin/bash currentDir=$( cd $(dirname "$0") pwd ) echo -n "current " pwd echo script $currentDir 

Since I posted the above answer a couple years ago, I’ve evolved my practice to using this linux specific paradigm, which properly handles symlinks:

ORIGIN=$(dirname $(readlink -f $0)) 

This will only resolve the symlink of directories. It will fail to resolve the symlink for a path. Consider when $0 is a link to a script in the same directory.

BASEDIR=$(readlink -f $0 | xargs dirname) 

Fancy operators are not needed.

You may try to define the following variable:

CWD="$(cd -P -- "$(dirname -- "$")" && pwd -P)" 

Or you can try the following function in Bash:

This function takes one argument. If the argument already has an absolute path, print it as it is, otherwise print $PWD variable + filename argument (without ./ prefix).

Answering this question very late, but I use:

SCRIPT=$( readlink -m $( type -p $ )) # Full path to script handling Symlinks BASE_DIR=`dirname "$"` # Directory script is run in NAME=`basename "$"` # Actual name of script even if linked 

Not all implementation of readlink have -m I believe the OP is looking for a solution that does not depend on GNU readlink extended functionality.

I’ve just tried a lot of these solutions, and this and @GreenFox ‘s answer are the only ones that work, when I have a symlink to the script in PATH, and call it like «bash -e my_script»

If you have to go for sudo -s source somescript.sh (i.e. fat32 partion without chance to set +x bit) this does not work ( while $ still does)

We have placed our own product realpath-lib on GitHub for free and unencumbered community use.

Shameless plug but with this Bash library you can:

This function is the core of the library:

function get_realpath() < if [[ -f "$1" ]] then # file *must* exist if cd "$(echo "$")" &>/dev/null then # file *may* not be local # exception is ./file.ext # try 'cd .; cd -;' *works!* local tmppwd="$PWD" cd - &>/dev/null else # file *must* be local local tmppwd="$PWD" fi else # file *cannot* exist return 1 # failure fi # reassemble realpath echo "$tmppwd"/"$" return 0 # success > 

It doesn’t require any external dependencies, just Bash 4+. Also contains functions to get_dirname , get_filename , get_stemname and validate_path validate_realpath . It’s free, clean, simple and well documented, so it can be used for learning purposes too, and no doubt can be improved. Try it across platforms.

Update: After some review and testing we have replaced the above function with something that achieves the same result (without using dirname, only pure Bash) but with better efficiency:

function get_realpath() < [[ ! -f "$1" ]] && return 1 # failure : file does not exist. [[ -n "$no_symlinks" ]] && local pwdp='pwd -P' || local pwdp='pwd' # do symlinks. echo "$( cd "$( echo "$" )" 2>/dev/null; $pwdp )"/"$" # echo result. return 0 # success > 

This also includes an environment setting no_symlinks that provides the ability to resolve symlinks to the physical system. By default it keeps symlinks intact.

Источник

Оцените статью
Adblock
detector