4

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?

2 Answers 2

4

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>
2
  • +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, 2016 at 9:17
  • For some reason, this will print the lines twice in zsh... Nov 21, 2020 at 18:19
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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .