I've found some existing code that will break if words in ${BASH_SOURCE[@]}
don't match the filepath given to '.' or 'source':
  [[ ${BASH_ARGV[0]} = "${BASH_SOURCE[0]}" ]]
which is part of a test to determine whether any args were provided after
"source filename".

I use this at the end of my file called «autoload.bash», which after
defining a function called «autoload», has:

if [[ -n "$*" ]] && ! {
>     (( BASH_ARGC[0] == 1 )) && [[ ${BASH_ARGV[0]} = "${BASH_SOURCE[0]}" ]]
> ||
>     (( BASH_ARGC[0] == 0 && ${#BASH_ARGC[@]} > 0 )) # in case this gets
> fixed sometime
>    }
> then
>     autoload -- "$@"
> fi


This is trying to figure out whether any arguments were provided after:

. "$path/autoload.bash"
>

It can't simply look at "$@" because when *no* args are given, the outer
args are left bound to "$@" (and can be modified by shift & set).

But unfortunately in this case, BASH_ARGC[0] is 1 rather than 0,, and the
filename provided to "source" (now ${BASH_SOURCE[0]}) is prepended to
BASH_ARGV. That's where the test for BASH_SOURCE[0] == BASH_ARGV[0] is
needed. It's still only probabilistic, because BASH_ARGV & BASH_ARGC have
some other weird behaviours, but it has a higher likelihood of being
correct, and in the cases where it's giving a false nagative, the
parameters would be invalid for my use case.

This test will clearly break if BASH_SOURCE does not contain exactly the
parameter given to "source" (or ".").

However this kludge is only necessary because of the pollution of
BASH_ARGC/BASH_ARGV, and my test will work correctly if that pollution is
removed.

I would be happy to always have $(realpath $0) or $(realpath
$sourced_filename) in BASH_SOURCE if there was also a concomitant change to
preface '0' onto ARGC (and not change BASH_ARGV) when a file is sourced (or
a function is called) without any args. However I worry that the latter
change might adversely affect someone else's code.

-Martin



On Thu, 27 Jun 2024 at 00:17, Martin D Kealey <mar...@kurahaupo.gen.nz>
wrote:

>
>
> On Wed, 26 Jun 2024, 03:14 Chet Ramey, <chet.ra...@case.edu> wrote:
>
>> On 6/19/24 6:12 PM, konsolebox wrote:
>>
>> > Alternatively, have BASH_SOURCE always produce real physical paths
>> > either by default or through a shopt.
>>
>> This is the best option. I don't think changing bash to do this by
>> default would have negative side-effects.
>>
>
> Just to be clear, would this result in $0 and ${BASH_SOURCE[@]:(-1):1}
> potentially yielding different values?
>
> (I would certainly hope so, otherwise how would we be able to alter the
> behaviour of a script based on the name used to invoke it? And before
> anyone tells me what a bad idea that would be, sometimes it's necessary to
> mimic historical behaviour such as gzip vs gunzip, and there are definitely
> scripts out there that rely on this.)
>
> However I do see a minor downside: looking at BASH_SOURCE to decide
> whether a given ancestor in the call chain is actually $0, the naive string
> comparison will now fail. This isn't important to most programs, but may be
> necessary for, say, a debugging framework.
>
> I have a small pure-Bash library that mimics Perl's "Carp.pm", providing
> "carp" , "cluck", "croak", and "confess". One feature I'm in the process of
> adding is suppression of specific "modules" from the backtrace displayed
> after a message; in this context I equate "module" with "source file".
>
> Since this is new for me, I can just document that users must use
> ${BASH_SOURCE[@]:(-1):1} rather than $0 when asking my "Carp" module to
> exclude its own "main" from backtraces.
>
> But if someone else has already implemented this, then their code will be
> subtly broken by this proposed change to Bash.
>
> -Martin
>
>>

Reply via email to