2

In Linux in Bash in a Script i use this:

#!/bin/bash

while true; do

    while true; do
        read -r -p 'enter number [4-999]: ' num

        if [[ $num =~ ^([4-9]|[1-9][0-9]{1,2})$ ]]; then
             break
        fi
    done

    while true; do
        result=$(( 5 * num * (1 + (RANDOM%9)) ))
        PS3="Is $result ok? "
        select yesno in "yes" "change your entered number" "generate a new random number" "exit this script" ; do
            case $REPLY in
                1|y) break 3 ;;
                2|n) break 2 ;;
                3|a) break ;;
                4|e) exit ;;

            esac
        done
    done

done

echo ...
echo 'rest of the script here'
echo ...

Have some questions about select

i become here:

1) yes                           3) generate a new random number
2) change your entered number    4) exit this script

how to change to

1) yes
2) change your entered number
3) generate a new random number
4) exit this script

and is this line correct?

    select yesno in "yes" "change your entered number" "generate a new random number" "exit this script" ; do

how to use the enter key instead of for example 1 ?

2
  • Not reproductible, bash 5.2.26, LinuxMint Vera, xfce4-terminal Commented Nov 21, 2024 at 4:46
  • 2
    Since the code in the question is entirely from unix.stackexchange.com/a/786798/70524, you should provide attribution
    – muru
    Commented Nov 21, 2024 at 9:06

1 Answer 1

5

bash tries to print the list in columns but using a bizarre algorithm.

It appears it tries to print it on as many columns as would fit on the terminal (finding out the width of the terminal from $COLUMNS or the result of ioctl(2, TIOCGWINSZ) if $COLUMNS is not set¹), except that if that would result in only one row, it prints it on one column instead.

So here, if you want the list on one column regardless of the width of the terminal, you can either set $COLUMNS to 1 (not 0 for which bash ignores the value and resorts to querying the terminal device) or to a very large number (COLUMNS=1 or COLUMNS=9999) though the latter may be less future-proof.

how to use the enter key instead of for example 1?

You can't. For that, you'd need to implement it by hand similarly to what you're doing for your number prompt.


BTW, in bash², for input validation, you can't use ranges in bracket expressions such as your [0-9], as those typically match hundreds of characters besides 0123456789. You need [0123456789] instead. For instance, in most locales, ¹²³ would not be rejected because those are characters that sort between 0 and 9, and you'd get something like line 12: ¹²³: syntax error: operand expected (error token is "¹²³") later on when that number is used in the arithmetic expression.

It's relatively harmless here, but it could get worse. For instance in a th_TH.iso885911 locale on a GNU system where (Thai digit 4) is between 0 and 9, is encoded as a single byte and classified as [:alpha:]:

$ LC_ALL=th_TH.iso885911 luit
$ env '๔๔=a[$(reboot)]' bash ./your-script
enter number [4-999]: ๔๔
System rebooting now...
1) yes
2) change your entered number
3) generate a new random number
4) exit this script
Is 0 ok?

๔๔ was not rejected because that matches [1-9][0-9], and in an arithmetic expression, since that's what the locale claims is 2 letters, was taken as a variable name, and its value was expanded into the arithmetic expression, causing reboot to be run.

More on that at Security Implications of using unsanitized data in Shell Arithmetic evaluation.


Also beware the behaviour of read is dependant on the current value of $IFS. With the default value of $IFS, leading and trailing space and tab characters are removed. If called in a context where $IFS may have been modified, it could have more annoying consequences, which is why when using read, it's good practice to set $IFS to the value you want such as:

IFS= read -rp 'Prompt: ' value

To not do any stripping. More on that at Understanding "IFS= read -r line".


¹ when run interactively bash sets $COLUMNS by itself, not in scripts.

² in its globs, and depending on the system and the C library bash was built against, in regexps used in [[ =~ ]].

1
  • 1
    I am impressed by the ๔๔ and thaï locale + evaluation in the result=$(( 5 * num * (1 + (RANDOM%9)) )) unwanted side effects ! (and it is beautifully explained). And when in other locales, variable names can begin with a character that matches a digit in that locale? Commented Nov 21, 2024 at 8:57

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.