3

Is there a way to return a specific value in an echoing function?

return allows me to return an exit status for the function. I need to return a more sophisticated data structure such as an array, or a string. Usually I would need to echo the value I want to be returned. But what if I need to echo informative messages in my function, and I only need to get the last echo which holds the result I need?

I have this chunk of code I would like to make a function out of it, but I would like to keep the informative echo as they are useful for guiding the user's input.

modules=(module1 module2 module3)
is_valid=-1
while [ $is_valid -lt 1 ] 
do
    echo "Please chose and order the available modules you need:"
    echo -e $(list_array_choices modules[@])
    echo -n "> "
    read usr_input
    choices=("$usr_input")
    is_valid=$(is_list_in_range choices[@] ${#modules[@]})
    [ "$is_valid" -eq -1 ] && echo -e "Error: your input is invalid.\n"
done

I would like to do something like

function get_usr_choices() {
    modules=${!1}
    is_valid=-1
    while [ $is_valid -lt 1 ] 
    do
        echo "Please chose and order the available modules you need:"
        echo -e $(list_array_choices modules[@])
        echo -n "> "
        read usr_input
        choices=("$usr_input")
        is_valid=$(is_list_in_range choices[@] ${#modules[@]})
        [ "$is_valid" -eq -1 ] && echo -e "Error: your input is invalid.\n"
    done
    echo ${choices[@]}  # This is the result I need.
}
choices=$(get_usr_choices modules[@])

Alas the echos completely mess up the output as I get a string containing all the echo, including the informative ones. Is there a way to do what I want a clean way?

3

You could output all the other content directly to screen assuming you don't ever want to do anything with it other than display.

Something similar to this could be done

#!/bin/bash

function get_usr_choices() {
        #put everything you only want sending to screen in this block
        {
                echo these
                echo will
                echo go
                echo to
                echo screen
        }> /dev/tty
        #Everything after the block is sent to stdout which will be picked up by the assignment below
        echo result
}
choices=$(get_usr_choices)

echo "<choices is $choices>"

Running this returns

these
will
go
to
screen
<choices is result>
  • +1. BTW, if you want to display other stuff as well as return a string, you can do that by opening another fd to /dev/tty and writing to that. e.g. i once wrote a wrapper script for openstack's glance command that both returned some of glance's output via echo and still allowed the glance progress bar to be displayed while it was uploading a VM image. i no longer have a copy of that script (belongs to a previous employer, not to me) but i'm sure you can find examples of similar things here on this site. – cas Apr 27 '16 at 9:17
2

Variables are not local in bash by default so you can just do:

function get_usr_choices() {
    modules=${!1}
    is_valid=-1
    while [ $is_valid -lt 1 ] 
    do
        echo "Please chose and order the available modules you need:"
        echo -e $(list_array_choices modules[@])
        echo -n "> "
        read usr_input
        choices=("$usr_input")
        is_valid=$(is_list_in_range choices[@] ${#modules[@]})
        [ "$is_valid" -eq -1 ] && echo -e "Error: your input is invalid.\n"
    done
}

get_usr_choices
# use choices here

The only gotcha is not to call get_usr_choices in a subshell by using $(...) or a pipe, otherwise you'll lose choices.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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