7

Im making a tool script for my theme has 6 options: 1) Check theme update 2) Reinstall theme 3) Install font 4) Install wallpaper 5) Check tool update 6) Quit

Here is the code

clear
echo "==========================="
echo "Tool for theme"
echo "==========================="

function check_update {
echo "checking theme update"
}

function reinstall_theme {
echo "Reinstalling"
echo "==========================="
}

function font {
echo "Installing font"
}

function wall {
echo "Installing wallpaper"
}

function check_update_tool {
echo "Checking tool update"
}

all_done=0
while (( !all_done )); do
options=("Check theme update" "Reinstall theme" "Install font" "Install wallpaper" "Check tool update" "Quit")

echo "Choose an option: "
select opt in "${options[@]}"; do
case $REPLY in
1) check_update; break ;;
2) reinstall_theme; break ;;
3) font; break ;;
4) wall; break ;;
5) check_update_tool; break ;;
6) all_done=1; break ;;
*) echo "Invalid option" ;;
esac
done
done

echo "Exiting"
sleep 2

But when I run it, the selections of menu mess up

==================
Tool for theme
==================
Choose an option:
1) Check theme update 2) Reinstall theme  3) Install font
4) Install Wallpaper     5) Check tool update      6) Quit

But what I want is

===============
Tool for theme
===============
Choose an option:
1) Check theme update
2) Reinstall theme
3) Install font
4) Install wallpaper
5) Check tool update
6) Quit

So how can I fix the menu?

5
  • Interesting, running that code (saved to foo.sh) on my system already produces your expected output if run with a dot . foo.sh, but produces the messed up output if run without it foo.sh.
    – agc
    Jul 3, 2016 at 15:45
  • Im running script on MobileTerminal on jailbroken iPhone, since iOS also use UNIX Jul 3, 2016 at 15:47
  • 1
    For more on this annoying shell behavior, along with a much worse kludge, see: Select menu in bash
    – agc
    Jul 3, 2016 at 16:12
  • Ok thanks, but a guy down here helped me Jul 3, 2016 at 16:15
  • I just want one column so iPhone can display properly Jul 3, 2016 at 16:15

1 Answer 1

17

You can set the COLUMNS variable to limit the width of the display, e.g., if you set it to 12, it will format your example into a single column:

COLUMNS=12
select opt in "${options[@]}"; do
case $REPLY in
1) check_update; break ;;
2) reinstall_theme; break ;;
3) font; break ;;
4) wall; break ;;
5) check_update_tool; break ;;
6) all_done=1; break ;;
*) echo "Invalid option" ;;
esac

produces

===========================
Tool for theme
===========================
Choose an option: 
1) Check theme update
2) Reinstall theme
3) Install font
4) Install wallpaper
5) Check tool update
6) Quit
#? 

The bash manual describes COLUMNS:

Used by the select command to determine the terminal width when printing selection lists. Automatically set if the checkwinsize option is enabled (see The Shopt Builtin), or in an interactive shell upon receipt of a SIGWINCH.

Besides seeing the feature in the manual page, it helps to read the source code to get the complete story. This variable is used in the select_query function, with the comment

/* Print the elements of LIST, one per line, preceded by an index from 1 to
   LIST_LEN.  Then display PROMPT and wait for the user to enter a number.
   If the number is between 1 and LIST_LEN, return that selection.  If EOF
   is read, return a null string.  If a blank line is entered, or an invalid
   number is entered, the loop is executed again. */

and later, in the select_query function

  t = get_string_value ("COLUMNS");
  COLS =  (t && *t) ? atoi (t) : 80;

If you give a reasonable value, atoi gives reasonable results (even zero in this case would be plausible, since that is less than 80 columns, and would be returned by atoi if you set COLUMNS to a nonnumeric value). If there's no value, (i.e. COLUMNS=""), bash uses 80 columns.

Further reading:

8
  • 1
    Works, but the reason given isn't correct. Experimenting with running the script with and without a leading dot: . foo vs foo, shows that unset COLUMNS causes the wrap, and any value for COLUMNS, i.e. COLUMNS=1 or COLUMNS=1000, or COLUMNS=woof produces the desired single column formatting. This looks like a bash bug.
    – agc
    Jul 3, 2016 at 16:28
  • Perhaps a bug, but the manual page says COLUMNS is used to determine the width. Jul 3, 2016 at 16:31
  • So, how wide is is COLUMNS=woof?
    – agc
    Jul 3, 2016 at 16:33
  • Scanning bug lists... Debian's BTS for bash, doesn't seem to have it. There's a poor documentation report from 2011 upstream.
    – agc
    Jul 3, 2016 at 17:08
  • Interesting code info, but the underlying code is mainly only relevant if one is composing workarounds and kludges. The code is not so relevant if it's a bug, as permitting illegal/silly values is just sloppy coding. Simile: for widely used buggy programs like bash its immense userbase becomes to its nervous maintainers a mass of easily angered fanatics, one that can turn against their masters for any small failure or change, which dread further enhances maintainer timidity.
    – agc
    Jul 3, 2016 at 17:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .