4

I am trying to have a sourced shell script determine its own location, and I have found that this is a difficult task for dash.

In bash, sh, and csh, I can use: $_.

In fish, I can use (status -f).

In dash, I have had no luck...

I have tried sourcing the path.sh file shown below with the following results:

# path.sh
called=$_
echo called:      $called
echo underscore:  $_
echo zero:        $0
echo dash_source: $DASH_SOURCE
echo bash_source: $BASH_SOURCE

dash -c ". path.sh"

outputs:

called:      /usr/local/bin/dash
underscore:  /usr/local/bin/dash
zero:        dash
dash_source:
bash_source:

How can I get the path to path.sh in dash?

user1338062
  • 11,939
  • 3
  • 73
  • 67

4 Answers4

5

I agree with the accepted answer, though I was able to make it work using lsof:

x=$(lsof -p $$ -Fn0 | tail -1); script=${x#n}

This works because dash keeps the script open when it is executing. This needs to be the first line of the script, otherwise it may fail if dash later opens additional file descriptors.

Paul Brannan
  • 1,625
  • 20
  • 20
  • 2
    it works for me with this minor modification: script=${x#*n} – ferdymercury Apr 05 '22 at 16:02
  • A bash compatible version of this would be: ```x="$(lsof -p $$ -Fn0 | tail -1 | tr -d "\0")"; script=${x#*n}``` (it elinimated the `command substitution: ignored null byte in input` warning) – Lockszmith May 25 '23 at 12:25
3

There does not appear to be any portable (POSIX-standard) way to do this.

POSIX refers to sourcing as "dot scripts." While several other parts of the shell language reference do discuss "dot scripts," none of these instances appear to provide any way to find out the path to the currently-executing dot script. In particular, $0 is reserved for "shell scripts" which are a different thing from "dot scripts." The word "source" does not appear on the page in any capitalization or as part of any larger word (so no $BASH_SOURCE-like thing). None of the standard environment variables which affect the shell seem relevant either. I'm going to say this is not possible within the POSIX spec. Since Dash follows POSIX quite closely, it is unlikely to have a Dashism for this specific case (if it was going to do that, it would've borrowed $BASH_SOURCE or created an analogous variable).

Kevin
  • 28,963
  • 9
  • 62
  • 81
  • So if a user happens to be running the dash-shell, I have to require them to source my file from a specific path? – StrugglingProgrammer Oct 06 '15 at 23:45
  • You could hard-code the path in the script, but that may not be workable in your case. Why do you need to know the path? Are there other files there you're going to go looking for? – Kevin Oct 06 '15 at 23:46
  • Exactly - I need to set an environment variable for the project home and then access some other files relative to that path. Hard-coding the absolute path is not an option. – StrugglingProgrammer Oct 06 '15 at 23:47
  • Hm... I wonder if you could have a "real" shell script do most of the heavy lifting and somehow get the sourced script to set the environment variable. I don't think that will work, though, because the dot script would need to know the path to the real script. I believe Python's Virtualenv gets around this by generating the dot script dynamically with another program and hard-coding the path at that time. – Kevin Oct 06 '15 at 23:51
  • Could you provide a link or some more information about how Virtualenv does this? – StrugglingProgrammer Oct 07 '15 at 00:03
  • [Here](https://github.com/pypa/virtualenv/blob/develop/virtualenv.py#L1521) is the function where the directory is actually substituted into the file content. In short, it puts a placeholder string in the dot script (whose actual contents may be found [here](https://github.com/pypa/virtualenv/blob/develop/virtualenv.py#L2026), albeit Base64-encoded to save space) and then expands that when the workspace ("virtual environment") is being initially set up. You set up the workspace once and then run the dot script every time you need to set environment variables and do stuff. – Kevin Oct 07 '15 at 00:09
  • Those links are no longer valid. [Here](https://github.com/pypa/virtualenv/blob/290f4b01f845a52b25450bcf7c025dc7eed513c1/virtualenv.py#L1374) is a permanent link to that function as it exists right now. – Kevin Oct 09 '15 at 18:42
1

This is not possible under POSIX shell standard (which dash implements, nothing more). . is a built-in, not a shell script, therefore, the $0 positional argument refers to the caller of ., and $1 refers to an argument to that caller, if one exists. In particular, none of these things refer to the . itself or its argument script.

Mark Galeck
  • 6,155
  • 1
  • 28
  • 55
0

If you can change the code that sources, put this function before all source calls.

.() {
   command . "$1"
}
. script.sh
# more dot scripts

what is does it to hook the 'dot script' function, now inside the function, positional parameters are the function parameters. So inside your sourced script, positional parameters are:

.         # $0
script.sh # $1

Inside the script being sourced:

# script.sh
echo "$(basename "$1")"     # this is the name of the script itself
pdg
  • 103
  • 8