[bash] Assign function result to var without suppressing it's stdout

I’d like to know how to assign function result (CLI select implementation in that case) without suppressing function’s stdout :upside_down_face:

Test2.sh

#!/bin/bash
title=$(basename "$0")
echo -n -e "\033]0;$title\007"

# Force output in english
export LC_ALL=en_US.UTF-8


readarray some_data << EOF
0 1.7.17 We
1 2.11 Are
2 3.1 Really
3 1.7.31 Doomed
EOF


main() {

    echo
    echo "   --------------------   Select   --------------------   "
    echo

    local result=$(CLI "list" some_data)

    [[ "${result}" != "" ]] && echo "${result}" || echo "Nothing selected"
}

CLI() {

    local mode=$1
    local -n args="$2"

    case "$mode" in
        list)
            local length=${#args[@]}
            for (( i=0; i < ${length}; i++ ));
            do
                echo "${args[i]}"
            done | column -t -s " " -R "1"
            echo

            local input
            while true;
            do
                [ -n "$input" ] || read -r -p "-> " input

                if [[ "${input}" =~ ^[0-9]+$ ]] && [ "${input}" -lt "${length}" ];
                then
                    local choice="${args[${input}]}"
                    echo "${choice}" | cut -d " " -f1
                    break
                else
                    local input=""
                    echo "Wrong option"
                fi
            done
        ;;
    esac
}

main "$@"

P.S. Sorry, that’s obviously stupid attempt, just for demo purpose - not sure how else to correctly ask that question :sweat_smile:

Separate definition of variable from the usage:

local result code
result=$(CLI ...)
code=$?
case "$code" in
   0) ;;
   1) echo "something wrong" ;;
esac

Is this what you mean?

No, not exactly if i get you correctly :upside_down_face:

What i wanted is CLI function to act something like separate program in a sense, for example like zenity behaves, but cli based in it’s interface and pure sh / bash…


If i’d describe it as human :robot: with words when i write that part:

local result=$(CLI "list" some_data)

I’d like to get:

  1. usual output of the function

    0  1.7.17  We
    1  2.11    Are
    2  3.1     Really
    3  1.7.31  Doomed
    
    -> 
    
  2. When selected by user input i’d like to return value of:

    local choice="${args[${input}]}"
    echo "${choice}" | cut -d " " -f1
    break
    

    So this value would’ve been written to variable:

    local result
    

    In case of that example value is int (0-3), but in reality it can be whatever string…


Problem in my example:

  1. When you just assign bash function to variable it will suppress output described in 1.

  2. Unlike if you just write:

    CLI "list" some_data
    

    But then how would you assign result to variable, except using some global variable maybe…?


Hopefully that makes sense :sweat_smile:

Being simpleminded, and not too much of a basher, I’d just find a way to do both - once enclosed in backticks for variable assignment. No idea if that does what you want, though :grin:

Sorry, I’m not really getting what you are trying to do…

1 Like

If you could somehow make the example simpler… As simple as possible, but not simpler than that! :wink:

Maybe the tee command?

1 Like

I don’t think i can go simpler than that :upside_down_face:

Check this out, if you diff that with OP and run both side by side - you’ll get what i mean:

#!/bin/bash
title=$(basename "$0")
echo -n -e "\033]0;$title\007"

# Force output in english
export LC_ALL=en_US.UTF-8


readarray some_data << EOF
0 1.7.17 We
1 2.11 Are
2 3.1 Really
3 1.7.31 Doomed
EOF


main() {

    echo
    echo "   --------------------   Select   --------------------   "
    echo

    CLI "list" some_data
    local result="${CLI_RESULT}"

    [[ "${result}" != "" ]] && echo "${result}" || echo "Nothing selected"
}

CLI() {

    unset CLI_RESULT
    local mode=$1
    local -n args="$2"

    case "$mode" in
        list)
            local length=${#args[@]}
            for (( i=0; i < ${length}; i++ ));
            do
                echo "${args[i]}"
            done | column -t -s " " -R "1"
            echo

            local input
            while true;
            do
                [ -n "$input" ] || read -r -p "-> " input

                if [[ "${input}" =~ ^[0-9]+$ ]] && [ "${input}" -lt "${length}" ];
                then
                    local choice="${args[${input}]}"
                    CLI_RESULT=$(echo "${choice}" | cut -d " " -f1)
                    break
                else
                    local input=""
                    echo "Wrong option"
                fi
            done
        ;;
    esac
}

main "$@"

This is pretty much exactly how i wanted it to work actually…But i thought maybe there is some more elegant method of doing so, than using global variable CLI_RESULT?

Would be cool to be able to get exactly same output as in that post, but with syntax of just assigning to variable:

local result=$(CLI "list" some_data)

Not sure if it’s possible at all


Very likely, i’m just not sure exactly how in that case

Do you mean you want to see the whole table as output, and also return the input value?

Could you simply output all you want to see into stderr (>&2 in bash)?

1 Like

Yep!

I could…So you mean use stderr as means of separation output / data?
It doesn’t feel right…But it will certainly work :upside_down_face:

Maybe I am missing something here but can’t you just let it be assigned to the variable and then echo it out?

1 Like

You can output to stdout (>&1) and stderr (>&2). On the terminal both go to terminal by default.

If you do

  result=$(CLI ...)

then result gets the output of stdout only (unless you redirected stderr…), and stderr output goes to terminal.

Hope this is what you are trying to do. :smile:

1 Like
  1. First way with global variable
  2. Second way with stderr / stdout

@dalto
Not sure i follow :sweat_smile:

Well you certainly can, like i did in OP demo?
Problem is - you make choice blind (without outputting table first) :sunglasses:

@manuel
Like i said, it feels wrong, coz it’s little hacky in terms of stderr usage :laughing:
But hey, it works!

Looks like your current code is way too complicated for the thing you are trying to do.
(Too) complex code easily leads to problems like this.

2 Likes

Well that’s not my actual code, it’s very reduced example to demo the intention (hence it looks very stupid :laughing: ) sorry.

OK, I know it isn’t easy to extract a simple example from code made by others.
Anyway, now you have at least 3 working answers to the question. :wink: