Checking if current file is being sourced using return yields reversed result after command returning 1

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP


Checking if current file is being sourced using return yields reversed result after command returning 1



I know the title is a bit weird, but I'm not sure how to better word it.



I'm using $(return &> /dev/null) to detect if a file is sourced or not.


$(return &> /dev/null)



I know that this method is not 100% reliable, but it has worked for me without issues before now. I've figured out how to work around the problem, but I can't figure out why this is happening.



Edit: This is for an internal company project and is not intended to be POSIX compliant or portable to other shells.



I've tried this on three different systems and had the same results:


Redhat 6.9; $BASH_VERSION=4.1.2(1)-release
Mint 18.2; $BASH_VERSION=4.3.48(1)-release
Arch; $BASE_VERSION=4.4.23(1)-release



If $? is 1 (false) when $(return &> /dev/null) the return code is reversed.


$?


$(return &> /dev/null)


$ hr | cat test_0source - test_0source.sh; hr; ./test_0source
#!/bin/bash
# test_0source

false
test_0source.sh

false
source test_0source.sh

true
test_0source.sh

true
source test_0source.sh
========================================
#!/bin/bash
# test_0source.sh

# shellcheck disable=SC2091
$(return &> /dev/null)
echo "$?"
========================================
1
1
1
0



I expected to be seeing


1
0
1
0



My workaround is to add true right before the return check. While this gives me the correct results, adding false instead of true cause the return check to always return 1.


true


return


false


true


return



I realize I'm missing something basic here, but I'm not seeing it. Why is this doing this?



Edit: I had an incorrect value for $? in my initial explanation above.



Update:
First, using $(return 0 &> /dev/null) resolves the problem, thanks to @ondre-k.


$(return 0 &> /dev/null)



Ondre also pointed out that comparing BASH_SOURCE against $0 is a more idiomatic way to do this:


$ hr | cat test_0bs - test_0bs.sh; hr; test_0bs | less
#!/bin/bash
# test_0bs

false
test_0bs.sh
hr

false
source test_0bs.sh
hr

true
test_0bs.sh
hr

true
source test_0bs.sh
========================================
#!/bin/bash
# test_0bs.sh

if [[ ${BASH_SOURCE[0]} == "$0" ]]; then
echo "We are not being sourced."
else
echo "We are being sourced."
fi
========================================
We are not being sourced.
========================================
We are being sourced.
========================================
We are not being sourced.
========================================
We are being sourced.



I suspect there may be an edge case or two around this, but it also seems to me that these cases will be less of a concern.





May I interest you in an alternative method? [[ "${BASH_SOURCE}" = ${0} ]] evaluates to 0 if file is not source and 1 if it is (test for not equal if you prefer it the other way around). Should run on bash pretty much anywhere.
– Ondrej K.
yesterday


[[ "${BASH_SOURCE}" = ${0} ]]


0


1


bash





Why not simply . file-to-source || { echo "source failed." >&2"; exit 1; } or handle the error however you like?
– David C. Rankin
yesterday


. file-to-source || { echo "source failed." >&2"; exit 1; }





I need to enforce 'file-to-source' be sourced. If you're writing a script and you forget to use 'source' the file that needs to be sourced needs to exit with an error message instead of blindly continuing.
– harleypig
yesterday





I got you. Also note source is an alias to the command '.', so you it appears your best check is the one suggested using ${BASH_SOURCE}, but note the BASH in the variable name, don't expect it to be POSIX compliant and portable to other shells.
– David C. Rankin
yesterday


source


'.'


${BASH_SOURCE}


BASH





Noted, and updated in the question.
– harleypig
yesterday




1 Answer
1



What you are doing is basically "abusing" the fact that return can only happen from function or a sourced script and assuming it yields 0 when a return was possible (we're sourced) and 1 if not, while suppressing the error output. You also do that in a subshell. Oddly enough, bash is still OK with placement of the return but does not return from a sourced script and keeps going. So far, so god.


0


1


return



The problem is, that "naked" (w/o explicit value specified) return, just like exit propagate last seen return code (return code of the command immediately preceding it).


return


exit



In other words false; return would be the same thing as return 1. You can also try this out by replacing true and false by (exit 255) and see what happens. The 1 you are seeing for return following a false is not the one of "error: you cannot say return now" as your test is looking for, but just an ordinary return that has returned the last from the command preceding it (false). This difference would also become obvious if you've dropped the stderr redirection.


false; return


return 1


true


false


(exit 255)


1


return


return


return


false


stderr



TL;DR for this construct to work as you expected, change it to return 0.


return 0



I hope I have not missed some corner case, but this should work as an alternative with bash by comparing name of the executed script and a source file. [[ "${BASH_SOURCE}" = ${0} ]] evaluates to 0 if file has not been sourced and 1 if it has. Or replace = with != to get same meaning of values as in the return case above.


[[ "${BASH_SOURCE}" = ${0} ]]


0


1


=


!=


return






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Keycloak server returning user_not_found error when user is already imported with LDAP

415 Unsupported Media Type while sending json file over REST Template

PHP parse/syntax errors; and how to solve them?