How to find Linux bash script path (Self Path)

Blog
Linux
Sys Administration

Writing smart programs using bash script is not easy, there are many issues along the way and one of them is what i'm going to describe here.

Sometimes it's require for bash scripts to know their location, for example when they are part of a package and there are other require files (on the same location) for them to run properly, and it's also not possible to change the path to where they're located simply because they also need the current directory.

After reading dozens of articles , testing results and combining them i came up with a simple function which can get the job done cleanly.

# Begin - Set script working dir and include dependencies
getScriptPath () {
	if [ -d ${0%/*} ]
	then
		abspath=$(cd ${0%/*} && echo $PWD/${0##*/})
		# to get the path only - not the script name - add
		pathOnly=`dirname "$abspath"`
	else
		progdir=`dirname $0`
		cd $progdir
		pathOnly=$PWD
	fi

	echo $pathOnly;
	return
}
currentPath=$(getScriptPath)
cd $currentPath
# End - Set script working dir and include dependencies

The function tested under CentOS and Ubuntu , i also like to kown if it works on other distributions as well. So feedbacks are welcomed

Update

Patric reviewed and wrote a tiny! version of this function and kindly shared it in comments ,i'm imporessed with how small it became!

getScriptPath () {
	echo ${0%/*}/
}
currentPath=$(getScriptPath)
cd $currentPath

Your rating: None Average: 1.8 (4 votes)

Comments

patrick's picture

Sorry, I can't leave that

Sorry, I can't leave that uncommented.

I don't like your function! :-)

line 05: ...$PWD/${0##*/}...
line 07: ...dirname...

In line 7 your removing the filename part ($PWD/${0##*/}) which you added in line 5 to abspath again - so why do you add it in the first place ? Simply

pathOnly=$(cd ${0%/*} && pwd)

would do the same thing as your 3 lines

lines 9 to 11:

pathOnly=$(cd $(dirname $0); pwd)

would do the same as your 3 lines
But its not really needed, as it will only be reached in case ${0%/*} was not a directory in the test in line 3. This is only true if $0 would be "/anything" - the first character a slash and no second slash. Then "${0%/*}" would be "". But to solve this it would be far easier to just add a slash to ${0%/*} when it's used in line 5 and forget the else clause and the if altogether.

So your function boils down to

getScriptPath () { cd ${0%/*}/; pwd; }

or the whole file to

cd ${0%/*}/

The rest is really superfluous.

:-)

What do others think?

admin's picture

Thanks Patrick for the

Thanks Patrick for the complete explanation, apparently you're an experienced shell programmer :)
The reason that it looks overdone is because i wanted to make sure that it works on both Debian and Red-hat based distributions. I don't remember exactly why i wrote it this way.

I tested and appended your version as well with a bit modification (i'm fan of generic codes), i'm sure people will find it very interesting just like me ;)

One more thing, what distru you're using?

patrick's picture

You're most welcome.

You're most welcome. Apparently you're an open-minded and honest person.

I started with RedHat in ... 1999. Then once rolled my own system from scratch. Then Debian, now Ubuntu.

admin's picture

Ubuntu is getting more

Ubuntu is getting more popular everyday, i'm also using it for more than 2 years.
Thanks for the kind words :), if you have a blog i would be happy to read your posts.

linda walsh's picture

That's excellent in its

That's excellent in its simplicity. But the wicked in me found it wouldn't work for a script in "/a b" without quotes around the ${0%/*} part. I also don't believe the trailing "/" is functionally necessary.

To make it useful in a script, you'd likely want to add it to a variable and not actually switch to the script dir.
That way your script could operate on the user's current dir (or use user-specified relative paths). You could just add the result to PATH:

export PATH="$(cd "${0%/*}" && echo $PWD)/lib:$PATH"

patrick's picture

Hello linda. You're perfectly

Hello linda.

You're perfectly right about the quotation missing marks. That's a bad flaw of many scripts and should be fixed.

The trailing slash (/): if you're wicked, you'll want that! As I said: "[...] only true if $0 would be "/anything" - the first character a slash and no second slash. Then "${0%/*}" would be "". But to solve this it would be far easier to just add a slash to ${0%/*} [...]" - $0 would be "/anything" IF the script you're running is in the filesystem root directory (maybe /init in a init-ramdisk - it's quiet uncommon of course but nevertheless possible - try it! '$ cd ""' - won't get you to "/", I think...

ok - I'm curious myself :-) - testing your func like this:

cat << 'EOF' > /dummytest
#!/bin/bash
unset PATH
export PATH="$(cd "${0%/*}" && echo $PWD)/lib:$PATH"
echo "\$PATH is '$PATH'"
EOF
chmod 755 /dummytest && $_

gives me:
$PATH is '/root/lib:'

yup - that means cd "" took me to /root, which is root's home directory. As cd without an argument takes you to your home directory. NOT "/".

As a sidenote: If you add elements to PATH (or some other environment variable) it's allways nice to check if it's not already there - otherwise it might add up - like so, maybe:

LIBDIR="$(cd "${0%/*}/lib" && echo $PWD)"
[[ $PATH =~ (^|:)$LIBDIR(:|$) ]] || PATH="$LIBDIR:$PATH"

Gabriel's picture

Hi, this is my

Hi, this is my way:

SCRIPT=$(echo $0 | sed 's/^.*\///')
FULLPATH=$(locate -e -L --regex /$SCRIPT$)
MIDIR=$(dirname $FULLPATH)