So here's my deal: working in BASH, I have already built out a function which works just fine that accepts an array or any number of parameters, and spits out an interactive menu, navigable by arrows up or down, and concluding with the user hitting enter, having highlighted the menu item they desire (the output of which is either the index or the menu item's value, depending on how the menu is initiated):
That's all working fine; I render the menu, then respond to the events parsed from the user's input to the invisible prompt of a read
command (auto-triggered after the collection of 3 characters):
read -s -n 3 key 2>/dev/null >&2
The output, having been fed into a $key
variable is then run through a case
statement evaluating against the predicted acceptable inputs:
\033[A
(up)
\033[A
(down)
""
(enter)
which in turn fire the behaviors desired.
However, it then dawned of me that with the introduction of 7+ menu items (we may presuppose it shall not exceed 10) it'd be nice to let the user tap the numeric entry of the desired menu item, which would highlight the item in question without submitting it.
My problem is this:
I got that working just fine too, BUT the user, having typed the numeric key desired (5 in the case of this example) is then obliged to hit the enter
key for the read
statement to trigger its effect, courtesy of my -n 3
modifier flags on my read
. This is counter to the usability model already established, unless, of course, they hit their numeric selection thrice, thereby triggering the 3-char minimum requirement (which is equally counterintuitive).
The issue is that \033[A
is treated as 3 characters, thereby necessitating the -n 3
.
0-9 are treated as SINGLE characters (meaning if I change that to a -n 1
, THEY behave as expected, but now the arrow keys fail, collecting only the escape character).
So, I guess what I'm wondering is: is there a way to listen for a -n 1 {OR} 3 (whichever comes first)
? I cannot seem to send a \n
or \r
or similar, as until the read
has resolved, they have no effect (meaning I have found no means to simply leave the -n 3
while running a parallel process to check if the entered value is a 0-9 should it prove a single character).
I'm NOT MARRIED to this approach. I'm fine with using awk
or sed
, or even expect
(though that last one I'm confused about still). I don't care if it's a read
that does the collecting.
Edit:
SOLUTION
read -n1 c
case "$c" in
(1) echo One. ;;
(2) echo Two. ;;
($'\033')
read -t.001 -n2 r
case "$r" in
('[A') echo Up. ;;
('[B') echo Down. ;;
esac
esac
Status: Resolved
@choroba to the rescue!
Solution Explanation
I'll do my best to paraphrase:
His solution involved nesting the two read
statements (I'd been trying them sequentially) It was this, coupled with the -t.001
(thereby setting a near-instant timeout on the function) that enabled the carryover read.
My problem was that the escape keys I'd been monitoring were 3 characters in length (hence my setting the -n3
flag). It wasn't until afterwards that it occurred to me that accepting certain single-character inputs would be advantageous, too.
His solution was to suggest a **($'\033')
case:
Basically
- 'Upon reading the escape character...' (
**($'\033')
) Create another
read`` (this time awaiting TWO characters), and set to timeout after a nanosecond and precluding backslashes on escape chars.
Since the behavior of read
apparently is to "spillover" the remaining input into the next read
statement, said statement started its countdown-to-timeout with the sought value having already been seeded. Since that met the defined requirement flags for the read
, then it became a simple matter of testing the second set of characters for the case result (and since the initializing function is still getting the response its expecting, albeit from a different statement, the program carries on as though it had gotten the results I'd been trying to puzzle my way to in the first place.
dialog
/whiptail
or shells with builtin support for ncurses likezsh
. – Stéphane Chazelas Nov 06 '18 at 12:48