1

I have a bash script containing a function which is sourced by a number of different bash scripts. This function may fail based on its input, and I'd like to create logging within the function to identify what script(s) are causing failures.

E.g., source /path/to/function.sh

The closest I've come is this:

ps --no-heading -ocmd -p $$

This works well enough if the full file path is used to run the parent script, returning:

/bin/bash /path/to/parent.sh

But it fails to provide the full path if the parent script is run from a relative path, returning:

/bin/bash ./parent.sh

Ideally, I'd like a way to reliably return the parent script file path for both cases.

I suppose I could have each parent script pass its file path to the function (via $0 or similar), but that seems hard to enforce and not terribly elegant.

Any ideas, or alternative approaches? Should I not worry about the relative path case, and just use full/absolute file paths for everything?

Thanks!

I'm using Centos 5.9. Bash version - GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)

1

3 Answers 3

1

You can use readlink to follow all symbolic links to get an absolute path.

echo $(readlink -f $0)
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, I didn't think to check that - seems to be the simplest solution.
1

As soon as the parent script starts export

 "`pwd`/$0"

or so, into an env variable, say ORIG_SCRIPT, then in the function just use ORIG_SCRIPT.

You need to do this as soon as the script starts because $0 may be relative to the PWD and if you later change PWD before you need the value of ORIG_SCRIPT, it gets unnecessarily complicated.

Update:

Since you know the pid by $$, you may get something from /proc/<PID>/cmdline but I don't know how exactly this one works right now.

Comments

0

You could use ${BASH_SOURCE[1]} to get the script that calls the function but that is not always on absolute path form. You could get the absolute path of it by readlink -m, realpath, or other shell-script based solutions, but if your script changes directory from time to time, conversion of relative paths to absolute paths would no longer be accurate as those tools base from the current directory to get the actual form.

There's a workaround however but this requires that you won't change directories in your scripts before calling (sourcing) the script that contains the function. You would have to save the current directory in that script itself then base forming of absolute paths through that directory. You are free to change directories after the script has already been included. As an example:

ORIGINAL_PWD=$PWD

function x {
    local CALLING_SCRIPT="${BASH_SOURCE[1]}"

    if [[ -n $CALLING_SCRIPT ]]; then
        if [[ $CALLING_SCRIPT == /* ]]; then
            CALLING_SCRIPT=$(readlink -m "$CALLING_SCRIPT")
        else
            CALLING_SCRIPT=$(readlink -m "$ORIGINAL_PWD/$CALLING_SCRIPT")
        fi

        echo "Calling script: $CALLING_SCRIPT"
    else
        echo "Caller is not a script."
    fi
}

Or

ORIGINAL_PWD=$PWD

function getabspath {
    local -a T1 T2
    local -i I=0
    local IFS=/ A

    case "$1" in
    /*)
        read -r -a T1 <<< "$1"
        ;;
    *)
        read -r -a T1 <<< "/$PWD/$1"
        ;;
    esac

    T2=()

    for A in "${T1[@]}"; do
        case "$A" in
        ..)
            [[ I -ne 0 ]] && unset T2\[--I\]
            continue
            ;;
        .|'')
            continue
            ;;
        esac

        T2[I++]=$A
    done

    case "$1" in
    */)
        [[ I -ne 0 ]] && __="/${T2[*]}/" || __=/
        ;;
    *)
        [[ I -ne 0 ]] && __="/${T2[*]}" || __=/.
        ;;
    esac
}

function x {
    local CALLING_SCRIPT="${BASH_SOURCE[1]}"

    if [[ -n $CALLING_SCRIPT ]]; then
        if [[ $CALLING_SCRIPT == /* ]]; then
            getabspath "$CALLING_SCRIPT"
        else
            getabspath "$ORIGINAL_PWD/$CALLING_SCRIPT"
        fi

        echo "Calling script: $__"
    else
        echo "Caller is not a script."
    fi
}

You could also play around with FUNCNAME and BASH_LINENO to be more specific with the errors. I'm just not sure if they're already supported in Bash 3.2.

If you actually had Bash 4.0+ you could make use of associative arrays to map absolute paths with it but if there are two scripts with the same names or are called with almost similar names, one value could be overridden. There's no fix to that since we can't choose our keys from BASH_SOURCE.

Added Note: You could also prevent your script from being unnecessarily sourced multiple times as it only requires to be once through a solution like Shell Script Loader. You might find convenience through it as well.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.