0

I'm trying to figure out how to pass an argument with a function call from one function to another in the same bash script. Here's what I've got so far:

#!/usr/bin/env bash
# File: nevens.sh

function isiteven {
    if (( "$element%2"=="0" ))
    then
        echo 1
    fi
}

function nevens {
    local result=0
    for element in $@
    do
        if (( $(isiteven) == 1 ))    # $(isiteven "$element")
        then
           result=$result+1
        fi
    done

    echo $result
}

I've tried calling $(isiteven) and hard-coding $element in front of %2==0 inside the isiteven function. And I've tried passing the argument with the function call, either $(isiteven $element) or $(isiteven "$element"), but then I'm not sure what I should code in front of %2==0 to do the math.

I'm using Ubuntu 18.04 on a dedicated machine.

Karl Baker
  • 903
  • 12
  • 27
  • 1
    BTW, if you defined `isiteven() { (( ( $1 % 2 ) == 0 )); }`, then you could just write `if isiteven "$element"; then`. That works because the default return value of a function is `$?`, and `if` branches on return value. This is **far** more efficient than making your function write to stdout, and then needing to capture that stdout through use of a subshell (thus requiring FIFO setup and a fork to create the subshell). – Charles Duffy Sep 19 '18 at 03:20
  • 1
    ...see this code running at https://ideone.com/mQgodw – Charles Duffy Sep 19 '18 at 03:23
  • @CharlesDuffy You could even use `isiteven() (( $1%2 == 0 ))`. – Benjamin W. Sep 19 '18 at 05:11
  • I ran that code, works great. I'm now trying to understand *how* it works. Here's what I think is going on: The `isiteven` function is a condition that returns either true (exits with 0) or false (exits with 1). These default return values from the function are held in a var named $?. Is this correct so far? Regarding `if` branches on return value: I'm guessing that means the `if` statement acts appropriately to either 0 or 1 being returned. – Karl Baker Sep 19 '18 at 05:21
  • @BenjaminW., thanks, your trimmed down version also worked for me. Can you tell me why this function does not require the curly braces as usual? – Karl Baker Sep 19 '18 at 05:41
  • 1
    @KarlBaker It's because the function body is a compound command, which includes conditional constructs (see [this article](http://www.catonmat.net/blog/bash-functions/)). While a neat tidbit to know, I'd argue that using it in any environment where anybody other than you will ever look at your shell code is not advisable, as the *vast* majority of people will be confused, and it really only saves a few keystrokes. – Benjamin W. Sep 19 '18 at 11:15
  • 1
    @KarlBaker Yes, your description above is accurate. (There are some minor quibbles to be had -- strictly, `$?` is a special parameter, not a variable -- but all the important aspects of your description were spot on). One bit of magic involved: For UNIX exit codes (as stored in `$?`), 0 is true and all other numbers are false (which is why `exit 1` or `return 1` is used for errors in scripts), but in an arithmetic context 0 is false and all positive integers are true. This gets fixed up in how `(( ))` translates its numeric results to UNIX exit codes: `(( 1 ))` sets `$?` to 0, and the inverse. – Charles Duffy Sep 19 '18 at 13:14
  • Thanks, Charles, I was a bit confused on how the `true` (`0`) result of the `(( ( $1 % 2 ) == 0 ))` condition ended up triggering `true` on the `(( $(isiteven) == 1 ))` condition that checks for equality with 1. Thanks for letting me know about the `(())` magic. – Karl Baker Sep 21 '18 at 00:23

1 Answers1

2

Just like you do for a scripts.

# Just an example to indicate to how to pass arguments.
isiteven() {
    echo "Command line arguments: $@"
}

nevens() {
    declare result=0
    declare element
    for element in "$@"; do
       # Do necessary logic here. 
       isiteven "$element"
    done
}
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
apatniv
  • 1,771
  • 10
  • 13
  • 1
    Consider POSIX rather than ksh function syntax: `isiteven() {` instead of `function isiteven {` will be more portable across POSIX-family shells. – Charles Duffy Sep 19 '18 at 03:13
  • 1
    BTW, `result=$result+1` would change `result=1` to `result=1+1`. Maybe you mean `result=$((result+1))`, to change it to `result=2`? – Charles Duffy Sep 19 '18 at 03:14
  • I was just addressing his/her main issue of passing argument rather than looking at his logic. You are right. Will fix it now. – apatniv Sep 19 '18 at 03:24
  • Thanks, @CharlesDuffy, that fixed my second (unstated) issue on the incrementer. And your advice on the POSIX syntax for declaring functions is helpful as I had read it was merely a matter of personal preference. – Karl Baker Sep 19 '18 at 04:47
  • @VivekAkupatni, thanks, my script works with `if isiteven "$element"`. – Karl Baker Sep 19 '18 at 05:36
  • The `()`s are strictly needed when not using a `function` keyword. – Charles Duffy Sep 19 '18 at 15:35