3

I am writing a library and would want to detect where from a RETURN trap was called. Kindly consider the following example:

#!/bin/bash

on_return() {
  echo "on_return: ${BASH_SOURCE[1]}:${BASH_LINENO[1]}:${FUNCNAME[1]}() returned" >&2
  declare -p BASH_SOURCE BASH_LINENO FUNCNAME
  echo
}
set -o functrace
trap 'on_return' RETURN

f() {
  return 101
}

f

. <(echo '
  echo "FROM INSIDE SOURCE:"
  declare -p BASH_SOURCE BASH_LINENO FUNCNAME
  echo
  f
  return 102
')

On Bash5.3 on archlinux this prints the following, with my comment:

on_return: ./test.sh:15:f() returned   # correct!
declare -a BASH_SOURCE=([0]="./test.sh" [1]="./test.sh" [2]="./test.sh")
declare -a BASH_LINENO=([0]="12" [1]="15" [2]="0")
declare -a FUNCNAME=([0]="on_return" [1]="f" [2]="main")

FROM INSIDE SOURCE:
declare -a BASH_SOURCE=([0]="/dev/fd/63" [1]="./test.sh")
declare -a BASH_LINENO=([0]="17" [1]="0")
declare -a FUNCNAME    # FUNCNAME is only set inside functions

on_return: ./test.sh:5:f() returned   # correct!
declare -a BASH_SOURCE=([0]="./test.sh" [1]="./test.sh" [2]="/dev/fd/63" [3]="./test.sh")
declare -a BASH_LINENO=([0]="12" [1]="5" [2]="17" [3]="0")
declare -a FUNCNAME=([0]="on_return" [1]="f" [2]="source" [3]="main")
 #                                           ^^^^^^^^^^^^ - here it is

on_return: ./test.sh:0:main() returned   # wrong, source returned
declare -a BASH_SOURCE=([0]="./test.sh" [1]="./test.sh")
declare -a BASH_LINENO=([0]="17" [1]="0")
declare -a FUNCNAME=([0]="on_return" [1]="main")  # no source in RETURN trap from source?

Because source is not present in FUNCNAME when sourcing a script, is there a way to know if RETURN trap is called from a source script return or from a function return?

1 Answer 1

3

The BASH_COMMAND variable could be used

Expands to the command currently being executed or about to be executed, unless the shell is executing a command as the result of a trap, in which case it is the command executing at the time of the trap. If BASH_COMMAND is unset, it loses its special properties, even if it is subsequently reset.

Showing BASH_COMMAND with declare -p the source command with its argument is shown by the last trap

#!/bin/bash

on_return() {
  echo "on_return: ${BASH_SOURCE[1]}:${BASH_LINENO[1]}:${FUNCNAME[1]}() returned" >&2
  is_source="$(declare -p BASH_COMMAND)"
  if [ "${is_source:25:2}" == '. ' ] || [ "${is_source:25:7}" == 'source ' ];then
    echo "source '${is_source:25:2}' return was trapped" >&2
  else
    echo "Command at trap: '${is_source:25}'" >&2
  fi
  echo
}
set -o functrace
trap 'on_return' RETURN

f() {
  return 101
}

f

. <(echo '
  echo "FROM INSIDE SOURCE:"
  declare -p BASH_COMMAND
  echo
  f
  return 102
')
on_return: ./ret.sh:20:f() returned
Command at trap: 'return 101"'

FROM INSIDE SOURCE:
declare -- BASH_COMMAND="declare -p BASH_COMMAND"

on_return: ./ret.sh:5:f() returned
Command at trap: 'return 101"'

on_return: ./ret.sh:0:main() returned
source '. ' return was trapped
Sign up to request clarification or add additional context in comments.

3 Comments

nice! "${is_source:25:2}" == '. ' just [[ "$BASH_COMMAND" = ". "* ]]
btw, fun fact, this does not work on bash 4.0, 4.1 and 4.2 . Probably a bug.
I wrote the answer on GNU bash, version 4.4.23

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.