46

I am writing a script to set environment variables on linux 2.6 using bash. So the script contains commands like:

export SRC_DIR=..
export LIBPATH=${SRC_DIR}/lib

the problem is that when i try to do echo $LIBPATH, it shows "../lib" as opposed to expanding the SRC_DIR to full path. I would really like the script to print something like /home/x/lib as opposed to ../lib.

UPDATE The script should evaluate SRC_DIR to be one directory upwards from the script's location and not the current directory from where the script is invoked

Jimm
  • 8,165
  • 16
  • 69
  • 118

9 Answers9

71

Change Directory in Subshell

There's a little trick you can use to get the absolute path from a relative path without changing the present working directory. The trick is to move to the relative path in a subshell, and then expand the working directory. For example:

export SRC_DIR=$(cd ..; pwd)

Relative Paths from Script Instead of Invocation Directory

To change to a relative path from a script's location, rather than the current working directory, you can use a parameter expansion or the dirname utility. I prefer dirname, since it's a little more explicit. Here are both examples.

# Using /usr/bin/dirname.
export SRC_DIR=$(cd "$(dirname "$0")/.."; pwd)

# Using the "remove matching suffix pattern" parameter expansion.
export SRC_DIR=$(cd "${0%/*}/.."; pwd)
Todd A. Jacobs
  • 81,402
  • 15
  • 141
  • 199
  • Sorry i spoke early. This has same issue that the script needs to be executed from the folder where it is located. But if i run the above script from /tmp, the SRC_DIR would evaluate to root folder such as / – Jimm Jul 23 '12 at 23:42
  • @Jimm PEBKAC. If you place your script in /tmp and then provide a relative path of ".." from the script's dirname, then of course you get /. You probably want "../src" or some other similar construction. – Todd A. Jacobs Jul 23 '12 at 23:58
19

The readlink command is capable of not only resolving symlinks, but also canonicalizing relative paths. Be careful, since you may not want the behaviour of resolving symlinks in all your scripts. If you don't want to resolve symlinks, pwd would be the best; note the use of a subshell, so the cd command does not affect the working directory in the main shell.

# The path you want to get information on... (for readability)
your_path=..

# Using /bin/readlink (resolves symlinks)
export SRC_DIR=$(readlink --canonicalize $your_path)

# Using /usr/bin/dirname (keeps symlinks)
export SRC_DIR=$(cd $your_path ; pwd)
mbells
  • 3,668
  • 3
  • 22
  • 21
10

I usually use

SCRIPT_DIR=$(readlink -f ${0%/*})

It should return the full path to the script, and even resolves all the links along the way.

choroba
  • 231,213
  • 25
  • 204
  • 289
  • 1
    This has same issue where $0 evaluates to word bash. My first line in script is #!/usr/bin/env bash. When i echo $SCRIPT_DIR, it does path expansion, but it also adds the word bash. I am executing the file directly by ./env.sh – Jimm Jul 23 '12 at 23:27
7

Digging up this old thread to add what i've found to work nicely:

export SRC_DIR = `realpath ..`

see more information here:

http://man7.org/linux/man-pages/man3/realpath.3.html

Lerring
  • 79
  • 1
  • 4
  • 1
    I'm downvoting this because it gets posted over-and-over again while not being a solution, because `realpath` is not part of the POSIX standard and therefore can't be relied upon on every system. – Bachsau Sep 29 '19 at 18:42
  • 3
    For most systems it's a better solution – Lerring Oct 01 '19 at 11:51
  • 2
    However, when `realpath` is available, it's the absoutely simplest and best possible solution. – Asbjørn Ulsberg Jul 31 '20 at 23:13
1

When I do this I use echo like so:

export SRC_DIR=`echo $HOME/bin/`
0

Do this instead:

export SRC_DIR=`pwd`;

Update:

Since you want path relative to the script's location on the filesystem, use this instead:

export SRC_DIR=`dirname $0`

Update2:

Your script must be invoked directly and not as bash /path/to/script.sh or source foo.sh. Add a shebang line, add execute permissions and invoke the script directly.

jman
  • 11,334
  • 5
  • 39
  • 61
  • 1
    The problem in pwd is that it would take the current directory from where you execute scripts. so i could be in /tmp and execute the above script in /home/x/scripts, it would take pwd as /tmp. But i would really like ../ to resolve to relative to script file location. Hence correct answer for ../ should be /home/x/ – Jimm Jul 23 '12 at 22:58
  • Oh if you wanted a path relative to script file location, you should specify that in the question! See updated answer. – jman Jul 23 '12 at 23:02
  • dirname $0 gives error, because $0 evaluates to the word -bash – Jimm Jul 23 '12 at 23:08
  • Are you invoking the script as `bash /path/to/script.sh`? If so, add `#!/usr/bin/env bash` as the first line of your script and invoke the script directly as `/path/to/script.sh` – jman Jul 23 '12 at 23:09
  • i am executing as source env.sh, where env.sh contains the variables – Jimm Jul 23 '12 at 23:13
  • This has different issue, where i need to supply the full path name to the script. Suppose i am in the same directory as the script and execute this command. The echo $LIBPATH would now show as ./lib. is there anyway to force . to expand to full file path? – Jimm Jul 23 '12 at 23:21
  • YOu get the error with `dirname $0` because you're invoking it as `source foo.sh`. You may want to try what I mentioned in Update2. – jman Jul 24 '12 at 01:04
0

For keeping error code:

function getPwd() {
    $(cd $1; [[ $? -ne 0 ]] && exit 1 || echo echo $PWD;)
    return $?
}
Zeph
  • 9
  • 1
0

None of the solutions here worked for me (for zsh). I came up with this:

#!/usr/bin/env bash

your_path="/Users/usr/Documents/Folder/Another/../../../Documents/SomeFile.dmg"

resolved=$(readlink -f "$your_path")
echo "$resolved"

Gives me:

/Users/usr/Documents/SomeFile.dmg

Note that the path in $your_path must exist.

c00000fd
  • 20,994
  • 29
  • 177
  • 400
0

With zsh, it's quite simple using the :P modifier:

export SRC_DIR=..
export LIBPATH=${SRC_DIR:P}/lib

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

smac89
  • 39,374
  • 15
  • 132
  • 179