KSH – Styling Text Based Menu Using STDERR

bashio-redirectionkshshellshell-script

Is it possible to format the STDERR in order to have a better looking menu using the select command?

I have a simple select

    select oChoice in $(<tempMenu.menu) ; do  
      case "$oChoice" in
          *) 
           break
          ;;
      esac
   done

I've tried a trick like:

    exec 3>&1
    select ...
    ...
    done 2>&1 1>&3 | sed 's/^/NICE OUTPUT    /'

But I cannot use escape sequences (i.e. colors), for example

   ...
   done 22>&1 1>&3 | sed 's/^/\033[1;33m\033[44mNICE OUTPUT    /'
   or
   done 22>&1 1>&3 | sed 's/^/\\033[1;33m\\033[44mNICE OUTPUT    /'

The escape sequences are not escaped and also the STDOUT is altered because I have customized the PS3 as well.

PS3="$(print \\n\\r)# $(print "\\033[1;33m\\033[44m")"$QUESTION"$(print "\\033[0m\\033[1;1m\\033[44m") `tput sc` $(print "")
#$(print "")
# $(print "")
# Status: $status $(print "")
# $(print "")
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  `tput rc` "

As far as I understand (Show only stderr on screen but write both stdout and stderr to file) I cannot separate the STDERR from the STDOUT, so, is there a smarter way to create dynamic text-based menus just using the STDOUT? (or an otherway to workaround my issue)

Best Answer

Revisiting an old question to provide a less effort implementation. In fact, two questions in one:

a) Handling STDERR in a select command

b) Change the appearance of items shown in the select command

Alternate approaches have been addressed already -- i.e. meaning solutions that don't use the select command. So I will not walk this path.

The select command is quite handy in that it automatically handles the column layout of your options, and essentially all you have to do is the case handling. So rather then sort of reinventing the wheel it can be a good solution for many simple cases.

a) Handling of STDERR

You have already the solution in the original question: redirect STDERR to STDOUT.

select choice in ...; do
...
done 2>&1

Both file handles offer the same capabilities, so there is no reason, IMHO, to fiddle around with this. Using STDERR for select is actually a good idea as you do not clobber your STDOUT and can use it for other purposes, such as, for instance, redirection.

b) Change the appearance of items shown in the select command

The solution to enhance the output is pretty trivial here too if you adopt a syntax in the likes of:

select choice in $(...); do
...
done

Use $(...) to perform whatever make up of the select items.

Your could for instance, on an ANSI terminal, colourise them:

$(for i in *; do print "\[E31m$i\E[0m"; done)

or call a colourisation function (which becomes reusable in other circumstances):

function colourise_diritems {
    typeset dir=$1 item

    for item in $dir/*; do
        if [[ -d $item ]]; then print "\E[31m$i\E[0m"
        elif [[ -f $item ]]; then print "\E[32m$item\E[0m"
        ...
        fi
    done
}

select choice in $(colourise_diritems $PWD); do
   ...
done

Obviously if you add markup to the items, you'll have to clean up the result before using it; in the above example, before using the selected choice, you would have to do something like

choice=${choice#\E[+([^m]*)m}
choice=${choice%\E[+([^m]*)m}

to remove the ANSI sequences.

Note: playing around with ANSI sequences can be tricky and requires further testing.

Related Question