path of the running script

Hi there,

After reading a couple posts on the subject, I came up with the compilation of all advice (that are supposed to work on any situation) and wrote the following script.
But tell me something... Is it that crazy that there's no other way to get the same solution!!!

#!/bin/bash
exec=$(ps -p $$ --no-heading -o args | cut -d' ' -f2-)
args="$@"; [[ ${#args} -gt 0 ]] && exec=${exec:0:${#exec}-${#args}-1}
exec=$(realpath "$exec")
echo "$exec"

If you have a better or simplier solution, I'd be happy not to be able to tell you that it doesn't work all the time.
Thx in advance
Santiago

Couple of comments:

  1. this is a very hard problem. Period. There are many ways to invoke a script
    Perderabo has a long discussion about this if you have not already seen the thread.

  2. Consider: Do not use exec as a variable name - it is a builtin in bash.

Thx jim mcnamara for your answer,
1) I searched the forum for "perderabo path running script" but could not find the thread you're talking about
2) Thanks for the advice

What's wrong with $0 (or $BASH_SOURCE if the script is being sourced (bash >= 3 only))?

Do you mean just the path, why not simply use:

dirname $0

if you want to assign it to a variable use

path_to_script=$(dirname $0)

or is this not what you mean?

S.

Thank you radoulov.
$0 will only give me the path to the file as the user called it. If my script is in the PATH, the user will call it by it's bare name and I will not be able to know the full path to it. I will study $BASH_SOURCE. What do you mean by (bash >= 3 only)?

Thank you Scrutinizer.
Same explanation as above. If the user calls my script by its bare name, dirname $0 will output nothing.

I suppose I misread the question,
do you need something like this (assuming POSIX shell):

$ ./s
--> /home/sysadmin/t
$ ./../t/s
--> /home/sysadmin/t
$ cat s
#! /bin/bash

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

printf -- '--> %s\n' "$p"

Thank you radoulov.
But here is one problem

~$ bash s
s: line 3: cd: s: Not a directory
-->

You wrote:

Is your previous code working in that case?

Perhaps I'm missing something but I cannot see how can you get the file position when invoked as shell scriptname and found somwhere in your path ...

It's Friday, sorry. I should have read the above comment before posting.

It works in any case because I use the result of the command

ps -p $$ --no-heading -o args

Which outputs something like that:

path_to_bash path_to_script arguments

This pattern will work in any case because even if it's not specified, a bash will always appear first :

$ bash s
bash s
$ ./s
/bin/bash s
$ s
/bin/bash /bin/s

I thought you wanted the path to the script even when invoked as shell scriptname:

bash /real/path/to/the/script

and not:

bash s

Hello chebarbudo,

Have you tried calling calling it "by it's bare name". If the script is somewhere in the search path then $0 should return the actual path to the script. Anyway, I a still not sure I understand what you mean. I think what you mean is to have a simple script that returns the absoute path to itself?

#!/bin/bash
echo $(cd $(dirname $0);pwd)

Should always do that I think..
If that is not what you mean could you state what you are looking for exactly?

S.

What I want is to be able to know the address (full path) of the script that is currently running without knowing how the user called it.

Thank you for your proposal Scrutinizer but once again, this is a script that will fail in some cases :

# /bin/s
/bin
# s
/bin
# bash s
/root

In the last case, this command fails.
So far the only command I found that works all the time is my own insanely complicated one.

I see, I think this should work then:

#!/bin/bash
dirname $(which $0)

If you require the path including the filename, this should suffice:

#!/bin/bash
which $0

I had this idea and thought it was great for a certain time untill I found a failure.
Consider the following script named test in your home directory:

#!/bin/bash
which $0

And now type the following commands:

$ ./test
/home/santiago/test
$ bash test
/usr/bin/test

The problem comes from the fact that there is another exe named test in the PATH.

It seems you can use the non-standard command realpath.
If I'm not missing something again it's quite easy in this case ...

% ./s
/home/radoulov/t
% bash s
/home/radoulov/t
% /home/radoulov/t/../../radoulov/t/s
/home/radoulov/t
% . s
/home/radoulov/t
% cat s
#! /bin/sh

p="$(realpath "$0")"

printf "%s\n" "${p%/*}"

This is correct, if it's in the path before your script, it IS the real running program when invoked as you describe.

Not quite true:

~# cat /root/s
#!/bin/bash
echo "I'm a user created script stored in /root/"
which $0
~# cat /usr/local/bin/s
#!/bin/bash
echo "I'm a user created script stored in /usr/local/bin/"
which $0
~# bash s
I'm a user created script stored in /root/
/usr/local/bin/s

Counter example:

~# cat /usr/local/bin/s
#! /bin/sh
echo "I'm a user created script stored in /usr/local/bin/"
p="$(realpath "$0")"
printf "%s\n" "${p%/*}"
~# bash s
I'm a user created script stored in /usr/local/bin/
s: No such file or directory