1

I have found the following code to return multiple variables from a function in bash:

#!/bin/bash
calc()
{
  A=$1
  B=$2
  total=$(( A + B ))
  diff=$(( A - B ))
  echo "$total $diff"
}
read TOT DIF < <(calc 5 8)
echo $TOT
echo $DIF

But when I run the script I get the following:

line 10: syntax error near unexpected token `<'

Any ideas how to solve this? I have tried the same code in a console and it works, the problem is when I run it from the file: sh myScript.sh.

2
  • 2
    sh (Bourne-shell) is usally not bash (Bourne-again shell). Commented Mar 15, 2020 at 2:06
  • 1
    < <(...) (process substitution) is a *bashism is isn't provided by POSIX shell. You need to ensure you are invoking bash (which your #!/bin/bash should do so long as you are not attempting to run as sh yourscript). If so, use bash yourscript. Commented Mar 15, 2020 at 2:12

2 Answers 2

3

You would do it this alternative way to work in non-bash shell:

#!/bin/sh

calc()
{
  A="$1"
  B="$2"
  total=$(( A + B ))
  diff=$(( A - B ))
  # Return ASCII EOT (code 3) separated values
  printf '%d\3' "$total" "$diff"
}
# Configure the Internal Field Separator to ASCII 03 End Of Text
IFS="$(printf '\3')"
# shellcheck disable=SC2046 # Intentionally split values
# Sets the arguments from the returned string
set -- $(calc 5 8)
TOT="$1"
DIF="$2"
echo "$TOT"
echo "$DIF"

One major drawback of calling the calc function in a sub-shell within the set arguments, is the loss of the function's shell return-code.

It means that it is up to the calc function to deal with erroring situations, like a division by zero.

For example: It can incorporate a return code within the returned IFS separated values.

But it means the error handling is much more complex.

I would not recommend implementing multiple return-values this way under non-bash shells.

It is still a valid option for a Bash shell to implement multi-return as an ASCII EOT delimited string, since the shell's return-code is preserved when reading and splitting the returned string the following way:

#!/usr/bin/env bash

# Call calc and parse returned split string into values
IFS=$'\3' read -r TOT DIF < <(calc 5 3)
# Save calc's return-code
_rc="$?"

# The return-code from the calc function is preserved
# and can be tested
if [ $_rc -ne 0 ]; then
  printf 'calc failed with return-code %d\n' "$_rc" >&2
else
  echo "$TOT"
  echo "$DIF"
fi

Sign up to request clarification or add additional context in comments.

Comments

2

You are explicitly running it with sh myScript.sh. In that case, the shebang #! is ignored because it is a comment to the shell.

Shebangs are detected by the program loader, not each individual shell. Were you to run your program it as ./myScript.sh (assuming it was flagged executable), the program loader would detect the shebang and convert the action to be /bin/bash myScript.sh automagically.

In other words, either use the specific shell you want to run it with (which would be bash in this case), or let the loader take care of it (see previous paragraph).

For example , here's a transcript of your code running with three different methods (specifically sh, specifically bash, and letting the loader take car of it - only the first one fails):

pax$ sh qq.sh
qq.sh: 10: qq.sh: Syntax error: redirection unexpected

pax$ bash qq.sh
13
-3

pax$ ./qq.sh
13
-3

Comments

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.