1

I need a function to show, only during debug, a message (the function name):

#!/bin/bash

debug_function(){
  if [[ $DEBUG -eq 1 ]]; then
    echo "${FUNCNAME[1]}"
  else
    DEBUG=0
  fi
}

test_function(){
 debug_function
 echo "something"
}

foo=$(test_function)

echo "Result: $foo"

It works, but in DEBUG=1 the function name is added to function output obviously.

# sh test
Result: something

# DEBUG=1 sh test
Result: test_function
something

Is there a way to print only a message?

Using wall I get the right behavior, but are there some other alternatives?

Thank you

Marco Cicala
  • 55
  • 1
  • 5
  • 1
    Note when you run `sh test` the shebang is totally irrelevant (see [this](https://unix.stackexchange.com/a/45048/108618)). Pure `sh` cannot run your code. Your `sh` apparently can, but this is "accidental". The code is for `bash` and the shebang looks right. Make the file executable and run it as `./test`. – Kamil Maciorowski Apr 16 '21 at 10:26
  • Totally right, just for test I din't set +x on script. – Marco Cicala Apr 17 '21 at 14:34

1 Answers1

1

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.

Kamil Maciorowski
  • 19,242
  • 1
  • 50
  • 94