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
...)
;;
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...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With