1

In shell script, echo can be used to return values from functions. But, if those functions want to print some messages. Then, this can be done by redirecting it to error stream. Below is a simplified example :-

#this is a function that returns a value, as well as
#print some messages
function logic(){
    echo >&2 "start of logic"
    echo >&2 "perform logic, to get value"

    echo "ok"
}

function smain(){
    local result=$(logic)

    echo "result is >$result<"

    if [ "$result" == "ok" ];then
        echo "script successful"
    else
        echo "script failed"
    fi
}

smain

Below is the sample execution output :-

sh sample.sh
start of logic
perform logic, to get value
result is >ok<
script successful

That works fine. But when this script is used as a autosys job. then messages from logic functions ends up in error stream file triggering alerts.

Is there any way, messages from logic function can be written to output stream and not mixing messages with return value.


Edit 1 :-

#!/usr/bin/env bash

function Return(){
    printf -v "$1" '%s' '$*'
}

function logic() {
    local arg=$1
    local system=$2
    echo "start of logic"
    echo "all params are >$*<"
    echo "perform logic, to get value"
    echo >&2 "logic successfully completed"
    printf -v "$1" '%s' 'abraKaDabra'
}

function main() {
    local result='' ;  logic "$@" result

    printf 'result is >%s<\n' "$result"

    if [ "$result" = "ok" ]; then
        echo "script successful"
    else
        echo "script failed"
    fi

    echo >&2 "end of main"
}

main "$@"

Output :-

$

sh returnValueWithDebugging.sh abc xyz > out.log 2>err.log

$

cat err.log
logic successfully completed
end of main

$

cat out.log
start of logic
all params are >abc xyz result<
perform logic, to get value
result is ><
script failed
mogli
  • 1,549
  • 4
  • 29
  • 57

2 Answers2

1

Would this work?:

#this is a function that returns a value, as well as                            
#print some messages                                                            
function logic(){
    echo "start of logic"
    echo "perform logic, to get value"

    echo "ok" >&2
}

function smain(){
    { local result=$( { { logic ; } 1>&3 ; } 2>&1); } 3>&1

    echo "result is >$result<"

    if [ "$result" == "ok" ];then
        echo "script successful"
    else
        echo "script failed"
    fi
}

smain
James Brown
  • 36,089
  • 7
  • 43
  • 59
  • request you to kindly explain, whats happening in { local result=$( { { logic ; } 1>&3 ; } 2>&1); } 3>&1 – mogli Jul 05 '20 at 09:12
  • got the idea, that you are swapping stderr and stdout streams – mogli Jul 05 '20 at 09:16
  • but didn't understand the brackets – mogli Jul 05 '20 at 09:16
  • The value to return in `logic()` is written to stderr (I guess you could use `>&4` and leave stderr for errors, didn't try). The `{}`s are just syntax when redirecting streams, first `1>&3` using `&3` as a temp, then stderr is redirected to stdout to get the return value to `$result` and finally `3>&1` returned to stdout for normal output. – James Brown Jul 05 '20 at 09:27
  • Hello James, posted a follow up question. https://stackoverflow.com/questions/62739537/simplify-function-call-that-writes-to-output-stream-and-returns-value. Thanks – mogli Jul 05 '20 at 10:35
1

I'd suggest you use the return code if the return of the function is a simple state indicator (like ok or fail).

And since you tag your question as Bash, returning arbitrary values can be achieved by reference rather than using an output stream, with the advantage of not calling your function within sub-shell processes:

Passing return value as reference (result variable name passed as argument 1):

#!/usr/bin/env bash
logic()
{
  echo "start of logic"
  echo "perform logic, to get value"
  printf -v "$1" '%s' 'ok'
}

function main ()
{
  result=''
  logic result

  printf 'result is >%s<\n' "$result"

  if [ "$result" = "ok" ];then
    echo "script successful"
  else
    echo "script failed"
  fi
}

main "$@"

Alternatively you can use name-ref variables with declare -n and write the logic function as:

logic()
{
  local -n res="$1"
  echo "start of logic"
  echo "perform logic, to get value"
  res='ok'
}

Another example with additional parameters used by logic:

#!/usr/bin/env bash
logic()
{
  local -n res="$1"
  local a="$2"
  local b="$3"
  echo "start of logic"
  echo "perform logic, to get value"
  if [ $a -gt $b ]; then
    res='ok'
  else
    res='oh no!'
  fi
}

function main ()
{
  result=''
  logic result 3 2

  printf 'result is >%s<\n' "$result"

  if [ "$result" = "ok" ];then
    echo "script successful"
  else
    echo "script failed"
  fi

  logic result 5 10

  printf 'result is >%s<\n' "$result"
}

main "$@"
Léa Gris
  • 17,497
  • 4
  • 32
  • 41