The macosxhints Forums

The macosxhints Forums (http://hintsforums.macworld.com/index.php)
-   UNIX - General (http://hintsforums.macworld.com/forumdisplay.php?f=16)
-   -   Can a Bash script determine where it is? (http://hintsforums.macworld.com/showthread.php?t=73839)

doubi 06-27-2009 05:47 PM

I have a further requirement. The last few scripts suggested do indeed give the location of the script as it runs.

However if you source the script instead of running it, this gives the current working directory from which you called the script, not the directory in which the script itself resides.

Here's the scenario: I have a number of slightly different versions of a project in different folders in my home directory, which I want to compile intermittently. Each version contains certain libraries and output folders which have been hard-coded into the build process (not by me) to be located using environment variables. What I want is to be able to have the same script sit in the top level folder of each of these different project versions, set the relevant paths for that version, relative to that top directory, and add them to the environment before I go to build. Ya dig?

Given that I need to source the script instead of having it run in its own execution environment, it seems as if the solutions suggested here fall just short of sufficient (although they are as thorough as any I have seen on this topic).

Any further ideas would be gratefully received :-)

Hal Itosis 06-28-2009 02:53 PM

New problem, new thread.

glyn 11-25-2009 05:38 AM

I think the following should find a scripts directory path:

path=$(cd ${0%/*} && pwd -P)

delainebrown 12-09-2009 06:57 PM

Here is a function I regularly use to change the directory to wherever the script resides.

Code:

WorkDir ()
{
# Change to Script Directory
WorkDir=`dirname "$0"`
cd "$WorkDir"
}


Hal Itosis 10-04-2010 07:37 AM

Quote:

Originally Posted by chriswaco (Post 503239)

You can't use $0 because that gives only a relative path if the script is run with a command like ./myscript.sh.

The method below works well for both Linux and Mac OS X. It's a bit weird, but works.

#!/bin/bash
#==========================
# bash - find path to script
#==========================
abspath=$(cd ${0%/*} && echo $PWD/${0##*/})

# to get the path only - not the script name - add
path_only=`dirname "$abspath"`

#display the paths to prove it works
echo $abspath
echo $path_only


Quote:

Originally Posted by LN2 (Post 523850)

I would change the line from the previous post:

abspath=$(cd ${0%/*} && echo $PWD/${0##*/})

to

abspath="$(cd "${0%/*}" 2>/dev/null; echo "$PWD"/"${0##*/}")"

This covers the case if the script was started with something like "bash yourscript.sh", which otherwise gives an error. It also adds some Quotes just to be on the save side if your $PWD contains /newlines or other 'ugly' characters.

Personally I prefer this method compared to the ones, that rely on "lsof -p" or "readlink -f", since it will probably work in a bash shell on almost any platform.


The excellent script posted by chriswaco (#19), combined with the smart fixes suggested by LN2 (#20) was the best solution so far... IMO. The simpler version offered by glyn (#23) was also good, but it didn't cover the bash yourscript.sh scenario (about which LN2 alerted us).

However... i found an (admittedly) obscure way to "break" them all. :)

If a symlink is created to the script, and we call upon that symlink...
then the parent we wind up with turns out to be the symlink's parent.
(maybe that's acceptable on occasion, but probably not in most cases).

Adding one more line (and one more variable) handles that potential symlinked scenario...
Code:


#!/bin/bash -
IFS=$' \t\n'
declare -x PATH=/bin:/usr/bin

# if the call came via symlink, then use its target instead:
arg=$0; [[ -L $0 ]] && arg=$(stat -f '%Y' "$0")

# now do exactly as chriswaco + LN2 said (minor syntax tweaks by me):
pth=$(2>/dev/null cd "${arg%/*}" >&2; echo "`pwd -P`/${arg##*/}")
par=$(dirname "$pth")

echo "script = $pth"
echo "parent = $par"
exit

Of course —if there are more levels of symlink abstraction (e.g., a symlink to another symlink to the file), then —even my method can be broken. At that point, perhaps the lsof process mentioned earlier by apokalyptik (#16) might be best for "missions to Mars" and crucial stuff like that. However, i reckon that more than one level of symlink misdirection would be extremely rare for an executable (though certainly not impossible).

AlexParamonov 11-19-2010 08:09 PM

I dug a ton of information and found a simple solution:
Code:

#!/bin/bash

script_dir_link=$(dirname "$(readlink "$0")")
if [[ $script_dir_link == "." ]]; then
    script_dir=$(dirname "$0")
else
    script_dir=$script_dir_link
fi

echo $script_dir

exit

=)

Hal Itosis 11-20-2010 02:50 PM

Quote:

Originally Posted by AlexParamonov (Post 601570)
I dug a ton of information and found a simple solution:
Code:

#!/bin/bash

script_dir_link=$(dirname "$(readlink "$0")")
if [[ $script_dir_link == "." ]]; then
    script_dir=$(dirname "$0")
else
    script_dir=$script_dir_link
fi

echo $script_dir

exit

=)

# saved it as "pth" in ~/bin/ and:

$ cd ~/Desktop
$ ../bin/pth
../bin

# ^ not an absolute pathname, so... not exactly a sufficient solution.

[also doesn't pass the symlink test (see previous post).]

cook 11-20-2010 03:58 PM

If you aren't allergic to tcsh, there it is simply $0:h

(More such modifiers are listed on the tcsh man page in the "history substitution" section.)

Just as a note, the original poster's application (like many others) doesn't care whether it's a full or relative path, and if he is using this approach in several directories, he would actually be wise to use symbolic links to a single updatable, replaceable script, in which case it should indeed use the directory containing the link, not the directory of the final script.

The following works on both Linux and OS X, when a script needs to be in its own directory.
Code:

#!/bin/tcsh

# $0:h has the final path component stripped (unless there was nothing else)
# so the following gets us to our 'source' directory
if ( $0:h != $0 ) cd $0:h


Hal Itosis 11-21-2010 02:17 PM

Quote:

Originally Posted by cook (Post 601626)
If you aren't allergic to tcsh, there it is simply $0:h

Let's begin by reading the thread's title, shall we?
>>> Can a Bash script determine where it is?


Quote:

Originally Posted by cook (Post 601626)
Just as a note, the original poster's application (like many others) doesn't care whether it's a full or relative path,

But then again, many posts (besides mine) do specify that goal... perhaps because determining the relative path was a trivial matter (already solved on page 1, also via some simple manipulation of $0).


Quote:

Originally Posted by cook (Post 601626)
and if he is using this approach in several directories, he would actually be wise to use symbolic links to a single updatable, replaceable script, in which case it should indeed use the directory containing the link, not the directory of the final script.

If you are indeed replying to "he" then, perhaps you should have quoted the relevant portions of "his" post. Again, the real tricky part here is that there are several different ways a script might be *invoked* by any given user. If the solution doesn't work for all of those differing methods of possible invocation... then it's not a real solution.


Quote:

Originally Posted by cook (Post 601626)
The following works on both Linux and OS X, when a script needs to be in its own directory.
Code:

#!/bin/tcsh

# $0:h has the final path component stripped (unless there was nothing else)
# so the following gets us to our 'source' directory
if ( $0:h != $0 ) cd $0:h


Again not Bash, so . . .

ganbustein 11-21-2010 04:40 PM

The real tricky part is that, because of links (both hard and soft), the concept of "THE name of a file" is a flawed concept. A script can no more tell where it "really is" than where any other file "really" is.

The best it can do is ask what path whoever launched it used to find it. If that path is relative, well, that's what you've got.

MrZehl 12-11-2010 04:37 PM

I don't have a Mac but I found this page while looking for the same. This works with Linux. Guess it works on a Mac too.

Code:

#! /bin/bash
echo "The absolute location of this script is $(dirname $(readlink -f $0))"


Hal Itosis 12-16-2010 01:10 AM

Quote:

Originally Posted by ganbustein (Post 601673)
The real tricky part is that, because of links (both hard and soft), the concept of "THE name of a file" is a flawed concept. A script can no more tell where it "really is" than where any other file "really" is.

The best it can do is ask what path whoever launched it used to find it. If that path is relative, well, that's what you've got.

I have to agree that hard links totally blow the notion of a precise (single) location into oblivion. But —from the tests i ran anyway —my version (in post #25) can overcome any abstractions caused by a symlink. [at least for one level of linkage].


All times are GMT -5. The time now is 05:42 PM.

Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2014, vBulletin Solutions, Inc.
Site design © IDG Consumer & SMB; individuals retain copyright of their postings
but consent to the possible use of their material in other areas of IDG Consumer & SMB.