6

This question already has an answer here:

I'm starting/trying to learn a bit of bash scripting and i'm wondering what is wrong with my method of passing arguments to a function from the terminal (see below) as my method seems similar to many found in internet tutorials.

#!/bin/bash

function addition_example(){
    result = $(($1+$2))
    echo Addition of the supplied arguments = $result
}

I call the script as follows:

source script_name.sh "20" "20"; addition_example 

This returns the following:

bash: +: syntax error: operand expected (error token is "+")

I've also tried:

addition_example "$20" "$20"

This returns the following:

bash: result: command not found
Addition of the supplied arguments =

marked as duplicate by Isaac, Jeff Schaller, Rui F Ribeiro, αғsнιη, Romeo Ninov Apr 13 '18 at 6:14

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

13

You are running the addition_example function with no arguments. Therefore, the $1 and $2 variables are empty and what you're actually executing is just result = $((+)). This gives precisely the error you mention:

$ result = $((+))
bash: +: syntax error: operand expected (error token is "+")

When you run source script_name.sh 20 20, the shell will source script_name.sh, passing it 20 and 20 as arguments. However, script_name.sh doesn't actually contain any commands, it only has a function declaration. So the arguments are ignored. Then, in a subsequent command, you run addition_example but with no arguments, so you get the error described above.

Also, you have a syntax error. You can't have spaces around the assignment operator (=) in shell scripts. You should change your script to:

function addition_example(){
    result=$(($1+$2))
    echo "Addition of the supplied arguments = $result"
}

And then run the function with the desired arguments:

$ source script_name.sh; addition_example 20 20
Addition of the supplied arguments = 40
  • 3
    Also (nitpicking): printf 'Addition ... = %d\n' "$(( $1 + $2 ))" (printf and no need for extra variable (which, if used, should be local)). One can also drop the function keyword to make it portable. – Kusalananda Apr 12 '18 at 14:28
  • 2
    It might also be worth mentioning that using "$20" as argument expands the second argument ($2) of the caller and appends a 0 at the end. – JoL Apr 12 '18 at 16:01
7

You have a few minor issues with the code.

Try this:

#!/bin/bash

function addition_example(){
    local num1=$1
    local num2=$2
    local result=$((num1+num2))
    echo "Addition of the supplied arguments = $result"
}
addition_example "$1" "$2"
  • The function was being declared in the script but when you simply execute the script it would not execute the function.
  • The arguments passed to the script (as positional parameters) will not be passed to the function unless specified.
  • You cannot have spaces around the = in variable assignment with bash
  • Your echo statement should be quoted, any variable should be quoted.
  • I didn't use the local keyword but will try it now to confirm it works also and investigate the difference for my own learning/progression. Not being able to have spaces around the '=' assignment operator caught me. Thanks for your help, I appreciate it. – MarkMark Apr 12 '18 at 14:37
  • @MarkMark Since this is your first time using local, it might be worth knowing that bash is dynamically scoped and not lexically scoped which is the more common among programming languages. What that basically means is that local variables of one function are available to all the functions that function calls and the functions they call, and so on. Such a property can be put to good use, but is best left avoided as many are not familiar with it. – JoL Apr 12 '18 at 16:17
  • @JoL Thanks, that's very interesting, I can imagine it's an extremely useful property but will stay away from it until I fully understand it's implications. If I understand you correctly (to put it another way for future readers): If calling function_a results in a call to function_b which in turn calls function_c then local variable 'var' defined in function_a can be used/modified etc... by function_c. – MarkMark Apr 12 '18 at 16:37
  • @JoL and MarkMark, I recommend always using local inside functions unless you have a specific reason not to. All variables set inside a function will be available in functions called by that function regardless, local prevents them from remaining available outside of the function. – Jesse_b Apr 12 '18 at 16:39
  • @Jesse_b and MarkMark, I would also recommend always using local inside functions. I didn't mean to recommend otherwise. What I meant to discourage was making use of dynamic scoping by expanding or setting a variable inside a function (without declaring it local there) that you know is local to a caller (or caller of the caller...) of that function. – JoL Apr 12 '18 at 17:34
2

There are several issues with the script.

Use Shellcheck

Some could be detected by using shellcheck.net. The first problem it shows is:

In dd line 4:
result = $(($1+$2))
       ^-- SC1068: Don't put spaces around the = in assignments.

Quote variables

You have a variable expansion unquoted. You should quote all variable expansions unless you really want that glob & split operations are needed. Change the echo line to:

echo "Addition of the supplied arguments = $result"

Do not use both function and () at the same time

The definition of the function could be either of this lines:

function add { 
add () {

The second method is more portable (runs on more shells).

Call the function with arguments

There are two methods to give parameters to the function.

Source it (not recommended)

You could source the script inside your present interactive shell:

$ source ./script_name.sh
$ addition_example 20 20
Addition of the supplied arguments = 40

Do not use $20 as the $ is a reserved character for the shell. It will be parsed by the shell as the second parameter $2 (in the present shell) and append a 0 after it.

However, this is changing the running shell with what is inside the script. Not something that you usually want.

Also, sourcing an script makes no use of the shebang line (the first line of the script). That will allow for any running shell in the interactive shell (sh,ksh,bash,zsh) to actually execute the code. If the code is written to be interpreted and executed with a specific shell.

Execute it

All you need to actually execute the script (as it should be) is:

  • Make the script executable chmod u+x script_name.sh
  • Include a shebang line (you already have one)
  • Do call the function.

Edited script

#!/bin/bash

addition_example(){
    result=$(($1+$2))
    echo "Addition of the supplied arguments = $result"
}

addition_example "$@"

And execute it as:

$ ./script_name.sh 20 20
Addition of the supplied arguments = 40

The "$@" get expanded to the list of arguments supplied to the script.

Improve robustness

To make your script more robust, test if the arguments to the function are empty (which will print an error message):

result=$((${1:-0}+${2:-0}))

Not the answer you're looking for? Browse other questions tagged or ask your own question.