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)

cwtnospam 06-16-2007 10:05 AM

Can a Bash script determine where it is?
 
Just trying to teach myself some more Unix, so what I'm trying to do is simulate this Applescript with a shellscript:
Code:

on adding folder items to this_folder after receiving added_items
        tell application "Finder"
                repeat with aItem in added_items
                        if modification date of aItem < (current date) then
                                set x to POSIX path of aItem
                                set y to "touch -m " & x
                                do shell script y
                        end if
                end repeat
        end tell
end adding folder items to

Here's what I have:

Code:

#!/bin/bash
a=~
f=$a"/desktop/downloads"
c=$f"/.update.txt"
d=$f"/.update2.txt"
g=$f"/.list1"
h=$f"/.list"
touch -m $h
ls $f > $g
diff $h $g | grep ">" > $c
sed -e 's_>\ _'"$f/"'_g' $c > $d
cp $g $h
sed -e 's/\ /\\\ /g' $d | xargs touch -m

I'd like to replace $f with the path of the script, which would be placed in the folder it is watching.

hayne 06-16-2007 10:26 AM

Quote:

Originally Posted by cwtnospam (Post 386370)
I'd like to replace $f with the path of the script, which would be placed in the folder it is watching.

The variable $0 contains the full path of the script being executed.

cwtnospam 06-16-2007 11:00 AM

<smacks head!>I had checked $1 through $9 :o

cwtnospam 06-16-2007 11:30 AM

Ok, is this the best way to remove the name and extension of the file 'testshell.sh' from $0?

Code:

t="/tmp/t"
echo $0 > $t
f=`sed -e 's_testshell.sh__g' $t`

Can I get $f directly from $0 without using a temp file? I tried using "$0" but that didn't work.

:o :o :o :o
EDIT: Never mind:
f=`dirname $0`

acr4 06-16-2007 11:31 AM

#! /bin/bash
f=$(pwd) # 'pwd' is "present working directory"
#all of your code here

cwtnospam 06-16-2007 11:51 AM

Odd that I didn't see your post even when I posted my edit, but you were 8 minutes before me!

Anyway, pwd returned '/etc' instead of the directory the script is located in.

biovizier 06-16-2007 12:09 PM

How about something along the lines of:
Code:

f=`dirname "$0"`

acr4 06-16-2007 12:57 PM

Quote:

Originally Posted by cwtnospam (Post 386402)
Odd that I didn't see your post even when I posted my edit, but you were 8 minutes before me!

Anyway, pwd returned '/etc' instead of the directory the script is located in.

Yeah you're right. Looks like 'pwd' returns the calling path. I.e. if you are in /etc, and you call /home/my.script, 'pwd' in the script will return /etc (or so I gather).

Anyway looks like the $0 method is the best bet.

Hal Itosis 06-16-2007 04:49 PM

Quote:

Originally Posted by biovizier (Post 386405)
How about something along the lines of:
Code:

f=`dirname "$0"`

Those "lines" would be the fewest. ;)

cwtnospam 06-16-2007 04:53 PM

I used f=`dirname $0` without the double quotes. Do those do anything in this case?

ataraxia 06-16-2007 08:24 PM

$0 only has the name of the script as you ran it. So, it only has the full path if that's the way you ran it. If you just used the short name you won't be able to get the full name while the script is running.

Hal Itosis 06-17-2007 04:40 PM

Quote:

Originally Posted by ataraxia (Post 386481)
$0 only has the name of the script as you ran it. So, it only has the full path if that's the way you ran it. If you just used the short name you won't be able to get the full name while the script is running.

You would need to bypass the $PATH mechanism
by typing "./scriptname" while inside the parent
(or by typing whatever relative path necessary).

So, just typing "scriptname" will typically produce
the full path (as it gets looked up in the $PATH).

-HI-

Hal Itosis 06-17-2007 05:02 PM

Quote:

Originally Posted by cwtnospam (Post 386445)
I used f=`dirname $0` without the double quotes. Do those do anything in this case?


If
it happened (which isn't often, I admit) that the script had
a space (or spaces) in its name... the results would be wrong.

If this is a script named "two words"...

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

echo `basename $0`
echo `dirname $0`
exit

...then, here is the result:

$ two\ words
two
usage: dirname path
$

The "two" is the unnoticed error from basename.
(The other is obvious).

Add quotes around "$0" to get it working.

-HI-

Acidprime 07-08-2007 04:38 PM

in case this helps someone , I use these varibles in all my scripts.
 
declare -x SCRIPTPATH="${0}"
declare -x RUNDIRECTORY="${0%%/*}"
declare -x SCRIPTNAME="${0##*/}"

bwiese 11-28-2007 09:43 AM

How to find and test the full path of the bash script being executed....
 
How to find and test the full path of the bash script being executed??? ....

I have not seen an answer to this posted anywhere else online and google brings up this page towards the top of a search, so I thought I better add this for prosperity:

myfp=`which $0`
mydir=`dirname $myfp`

Yes, the "which" command holds the key! :) Also, you can test that a string is a full path by testing if the first character is a "/" or not (on unix) with a test like...

if [ "/" == "${mydir:0:1}" ]; then
echo "full path = $mydir"
fi

I like working with full paths for all of my filesystem work... makes debugging easier at least!

apokalyptik 03-02-2008 10:59 PM

The most reliable indicator of this attribute that I have found is to use LSOF. I tend to care about this on Linux and not OSX, but since this is very high in the google rankings for a search on "bash full path to file" I'm posting it here.

Code:

#!/bin/bash
## Linux
LSOF=$(lsof -p $$ | grep -E "/"$(basename $0)"$")
MY_PATH=$(echo $LSOF | sed -r s/'^([^\/]+)\/'/'\/'/1 2>/dev/null)
if [ $? -ne 0 ]; then
## OSX
  MY_PATH=$(echo $LSOF | sed -E s/'^([^\/]+)\/'/'\/'/1 2>/dev/null)
fi

MY_PID=$$
MY_ROOT=$(dirname $MY_PATH)
MY_NAME=$(basename $0)

echo -e "PATH\t$MY_PATH"
echo -e "FILE\t$MY_NAME"
echo -e "CWD \t$MY_ROOT"
echo -e "PID \t$MY_PID"

Code:

.oO(apokalyptik@apok-mbp-en.lan ~) uname -a
Darwin apok-mbp-en.lan 9.2.0 Darwin Kernel Version 9.2.0: Tue Feb  5 16:13:22 PST 2008; root:xnu-1228.3.13~1/RELEASE_I386 i386 i386
.oO(apokalyptik@apok-mbp-en.lan ~) ./foo.sh
PATH    /Users/apokalyptik/foo.sh
FILE    foo.sh
CWD    /Users/apokalyptik
PID    16347


Abu 09-10-2008 02:58 PM

One Liner
 
Try this one:

dir=$(dirname $(which $0));

hayne 09-20-2008 11:35 AM

Use of the 'which' command in this context is not useful.
The 'which' command merely looks in the PATH and tells you which version of the command (given as an arg) is first in the PATH.

If you ran a script via its full pathname (e.g. /Users/Scripts/foo) then the PATH is irrelevant and so the result of using the 'which' command (as some people have suggested above) can be misleading (e.g. if the PATH does not include /User/Scripts in the above example).

chriswaco 11-14-2008 09:57 AM

This is the best way
 
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.

Code:

#!/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


LN2 03-13-2009 12:44 AM

I would change the line from the previous post:
Code:

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

Code:

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.

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.