Your echo "${FUNCNAME[1]}"
prints to stdout, so its output belongs to the output of the debug_function
, which in turn belongs to the output of the test_function
.
Print the message to stderr:
by redirecting the output of echo
:
echo "${FUNCNAME[1]}" >&2
or by redirecting the entire output of debug_function
(if the whole function should print only to stderr) when you call debug_function
:
debug_function >&2
The latter method allows commands inside the debug_function
to print straightforwardly to stdout. What they print will be redirected because stdout of the debug_function
is redirected.
The function itself can redirect its stdout to its stderr with exec >&2
. This will affect all commands in the function. But this will also affect the calling function/script, unless the redirecting function is run in a subshell.
You can run the function in a subshell on demand with (debug_function)
. To always run the function in a subshell, use ()
instead of {}
when defining the function.
So a function whose entire purpose is to print to stderr and not to stdout can be defined like:
debug_function()(
exec >&2
echo whatever
# ...
)
Now echo
and other commands in the function don't need >&2
. Even if you call the function simply as debug_function
, the commands in it will print to stderr.
Note: stdout or stderr of one subshell or command can be different than stdout or stderr of another subshell or command or the script as a whole. Redirections are possible on many levels. Therefore even a function that does exec >&2
in its own subshell can ultimately write to stdout of the whole script, if "proper" redirections happen outside of the function body. I won't elaborate.
The most important thing to remember:
Stdout is often captured (like in your case) or piped further. The main point of stderr is not to let error/debug/diagnostic messages pollute stdout.
sh test
the shebang is totally irrelevant (see this). Puresh
cannot run your code. Yoursh
apparently can, but this is "accidental". The code is forbash
and the shebang looks right. Make the file executable and run it as./test
. – Kamil Maciorowski Apr 16 '21 at 10:26