0

Given the python script

if __name__ == '__main__':
    print("first")
    print("second")
    print("third")

the bash script

#!/usr/bin/env bash

declare -a choice=$( python3 test.py )
echo "You chose "
for c in "${choice[@]}"; do
    echo "> ${c}"
done

should print

You chose
> first
> second
> third

but instead, it prints

You chose
> first
second
third

Why is that, and how do I fix this?

3

The Python script prints three lines with text. To read these into an array in bash, use readarray:

readarray -t chose < <( python3 test.py )
printf '> %s\n' "${chose[@]}"

The readarray -t command will read lines into the given array from standard input. Standard input is redirected from your Python script using a process substitution. The readarray utility will by default read individual lines, i.e. strings of text terminated by a newline, and the -t option makes the utility remove the terminating newline from the data that is read.

The printf call prints each array element with a leading > and space. Giving multiple arguments to printf, which you do by giving it the expansion of an array like this, it will reuse its formatting string for outputting each individual argument. This means there is no need for a loop.


The issue with your script is that you are reading the output from your Python script into a single string. The command substitution $( python3 test.py ) would be expanded to a string that you then would have to manually parse and split on newlines into the correct array elements.

You could do as Dabombber suggests and let the shell do this splitting for you with choice=( $( python test.py ) ) (the declare -a is not needed), but note that this would split the string on any whitespace (spaces, tabs and newlines (the contents of $IFS) by default), which may not be what you want if you want your individual array elements to include spaces.

An option then would possibly be to set IFS to a newline to get the shell to split the data on newlines only, or to read the output for the Python code with read, and these may be good solutions, but you have the readarray built-in utility in bash which makes this easier.


It looks (judging from the variable name and other text in your code) that you may want to implement some sort of interactive menu.

This could also be done with select in bash:

readarray -t options < <( python3 test.py )

echo 'Please select your option' >&2
PS3='Your selection: '
select ch in "${options[@]}"; do
    [ -n "$ch" ] && break
    echo 'Invalid, try again' >&2
done

printf 'You chose option %d ("%s")\n' "$REPLY" "$ch"

This would use the output of your Python script as options in a select loop. The loop would continue until a valid option has been selected by the user. At the end, the picked option (and its number) is printed.

The PS3 prompt is the prompt used by select in bash. The default value is #? followed by a space.

3

Looks like declare -a choice=$( python3 test.py ) making a 1 length array where

choice[0]='first
second
third'

Try declare -a choice=( $( python3 test.py ) )

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.