Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Select menu in bash

For some reason, my menu renders like this:

1) RUNTHROUGH         6) option 4
2) QUIT               7) option leg
3) NEW SEARCH PATERN    8) option arm
4) CLEAR SEARCHES             9) option whatever
5) option 1                          10) option blabla

,but I would like it to be aligned properly, like this:

1) RUNTHROUGH         6) option 4
2) QUIT               7) option leg
3) NEW SEARCH PATERN  8) option arm
4) CLEAR SEARCHES     9) option whatever
5) option 1          10) option blabla

How can I do that?

I am populating menu from two arrays:

One is constructed from label variables like this:

MENU_OPTIONS=( "$SCRIPT_RUNTHROUGH" "$PROGRAM_QUIT" "$PATTERN_NEW" "$PATTERN_CLEAR" )

where variable has value like this, with color escapes

SCRIPT_RUNTHROUGH=$'\e[1;31mRUNTHROUGH\e[0;33m'
PROGRAM_QUIT=$'\e[1;31mQUIT\e[0;33m'
PATTERN_NEW=$'\e[1;31mNEW SEARCH PATERN\e[0;33m'
PATTERN_CLEAR=$'\e[1;31mCLEAR SEARCHES\e[0;33m'

and another array entries is coming from file:

readarray -t entries < ./file

content of file is raw text data:

option 1
option 4
option leg
option arm
option whatever
option blabla

so select block goes like:

select opt in "${MENU_OPTIONS[@]}" "${entries[@]}";
do
    case "$opt" in
        ...)
        ;;
like image 823
branquito Avatar asked Dec 08 '25 07:12

branquito


1 Answers

Bash mostly count the char in each option, regardless they are "displayable" or not. So, the escape code used to highlight some of your items false the display.

Here is an example:

sh$ for i in {1..12}; do OPTIONS[$i]=$(printf "option-%02d" $i); done
sh$ select i in "${OPTIONS[@]}"; do echo $i ; done
1) option-01    4) option-04   7) option-07  10) option-10
2) option-02    5) option-05   8) option-08  11) option-11
3) option-03    6) option-06   9) option-09  12) option-12

This is as expected. But if I highlight the 4th item:

sh$ OPTIONS[4]=$'\e[1;31moption-04\e[0;33m'
sh$ select i in "${OPTIONS[@]}"; do echo $i ; done
1) option-01    4) option-04           7) option-07  10) option-10
2) option-02    5) option-05   8) option-08  11) option-11
3) option-03    6) option-06   9) option-09  12) option-12

WORKAROUND

Well ... it's more a hack than a workaround. Really.

I add some extra spaces at the end of every option in order to widen the columns. Instead, I add a few \b at the end of the highlighted option (6 gave good results on my tests). I can't be more precise, since I wasn't able to clearly determine the rule to follow. You probably have to be prepared for trial and error...
(thing are even more complicated by the fact Bash uses a mix of tab and space to indent the various options)

Anyway, this display properly on my system:

sh$ for i in {1..12}; do OPTIONS[$i]=$(printf "option-%02d        " $i); done
#                                                         ^^^^^^^^
sh$ OPTIONS[4]=$'\e[1;31moption-04\e[0;33m\b\b\b\b\b\b'
#                                         ^^^^^^^^^^^^
sh$ select i in "${OPTIONS[@]}"; do echo $i ; done
1) option-01            5) option-05           9) option-09        
2) option-02            6) option-06          10) option-10        
3) option-03            7) option-07          11) option-11        
4) option-04            8) option-08          12) option-12 

EDIT: I was puzzled by the fact in my example, the menu was off by 8 char. Doesn't match with the number of bytes in the various escape codes.

But, given the code from GNU bash 4.2 : (execute_cmd.c)

static int
displen (s)
     const char *s;
{
#if defined (HANDLE_MULTIBYTE)
  wchar_t *wcstr;
  size_t wclen, slen;

  wcstr = 0;
  slen = mbstowcs (wcstr, s, 0);
  if (slen == -1)
    slen = 0;
  wcstr = (wchar_t *)xmalloc (sizeof (wchar_t) * (slen + 1));
  mbstowcs (wcstr, s, slen + 1);
  wclen = wcswidth (wcstr, slen);
  free (wcstr);
  return ((int)wclen);
#else
  return (STRLEN (s));
#endif
}

It appears that if Bash is compiled with multibyte support, it converts the raw string using mbstowcs in order to calculate its length using wcswidth. Given my system has UTF-8 LC_CTYPE, probably mbstowcs misconverted the escape code as some Unicode character...

like image 101
Sylvain Leroux Avatar answered Dec 10 '25 22:12

Sylvain Leroux



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!