howtechstuffworks
howtechstuffworks

Reputation: 1926

Bash Function is not getting called, unless I echo the return value

In my program I am trying to return a value from a function, the return value is string. Everything works fine(atleast some part), if I echo the value once it is returned, but it is not even calling the function, if I dont return.... Consider the code below....

#!/bin/bash

function get_last_name() {
    echo "Get Last Name"
    ipath=$1

    IFS='/'
    set $ipath
    for item
            do
                    last=$item
            done

    echo $last
}

main() {
    path='/var/lib/iscsi/ifaces/iface0'
    current=$(get_last_name "$path")
    echo -n "Current="
    echo $current
}


main

It gives me an output like this

OUTPUT
     Current=Get Last Name iface0

If I comment the echo $current, then the I am not even seeing the "Get Last Name", which makes to come to conclusion, that it is not even calling the function. Please let me know what mistake I am making. But one thing I am sure, bash is the ugliest language I have ever seen.......

Upvotes: 2

Views: 6421

Answers (1)

chepner
chepner

Reputation: 532368

Functions do not have return values in bash. When you write

current=$(get_last_name "$path")

you are not assigning a return value to current. You are capturing the standard output of get_last_name (written using the echo command) and assigning it to current. That's why you don't see "Get last name"; that text does not make it to the terminal, but is stored in current.


Detailed explanation

Let's walk through get_last_name first (with some slight modifications to simplify the explanation):

function get_last_name () {
    ipath=$1

    local IFS='/'
    set $ipath
    for item
    do
        last=$item
    done

    echo "Get Last Name"
    echo $last
}

I added the local command before IFS so that the change is confined to the body of get_last_name, and I moved the first echo to the end to emphasize the similarity between the two echo statements. When get_last_name is called, it processes its single argument (a string containing a file path), then echoes two strings: "Get Last Name" and the final component of the file path. If you were to run execute this function from the command line, it would appear something like this:

$ get_last_name /foo/bar/baz
Get Last Name
baz

The exit code of the function would be the exit code of the last command executed, in this case echo $last. This will be 0 as long as the write succeeds (which it almost certainly will).

Now, we look at the function main, which calls get_last_name:

main() {
    path='/var/lib/iscsi/ifaces/iface0'
    current=$(get_last_name "$path")
    echo -n "Current="
    echo $current
}

Just like with get_last_name, main will not have a return value; it will produce an exit code which is the exit code of echo $current. The function begins by calling get_last_name inside a command substitution ($(...)), which will capture all the standard output from get_last_name and treat it as a string.

DIGRESSION

Note the difference between the following:

current=$(get_last_name "$path")

sets the value of current to the accumulated standard output of get_last_name. (Among other things, newlines in the output are replaced with spaces, but the full explanation of how whitespace is handled is a topic for another day). This has nothing to do with return values; remember, the exit code (the closet thing bash has to "return values") is a single integer.

current=get_last_name "$path"

would not even call get_last_name. It would interpret "$path" as the name of a command and try to execute it. That command would have a variable current with the string value "get_last_name" in its environment.

The point being, get_last_name doesn't "return" anything that you can assign to a variable. It has an exit code, and it can write to standard output. The $(...) construct lets you capture that output as a string, which you can then (among other things) assign to a variable.

Back to main

Once the value of current is set to the output generated by get_last_name, we execute two last echo statements to write to standard output again. The first writes "Current=" without a newline, so that the next echo statement produces text on the same line as the first. The second just echoes the value of current.

When you commented out the last echo of main, you didn't stop get_last_name from being executed (it had already been executed). Rather, you just didn't print the contents of the current variable, where the output of get_last_name was placed rather than on the terminal.

Upvotes: 8

Related Questions