If you are using a newer Bourne-compatible shell such as (pd)ksh or bash, and have a modern terminal, you can take advantage of the -icanon stty mode to good effect. This mode allows your scripts to respond to a single keystroke rather than requiring the user to hit “return” after entering a character. This is useful for the familiar “Press any key to continue … ” type messages, but can also be used to provide control of programs.
|
1 2 3 4 5 6 7 8 9 10 11 12 |
#!/bin/ksh tput smso echo -e "Press any key to continue...\c" tput rmso oldstty=`stty -g` stty -icanon -echo min 1 time 0 dd bs=1 count=1 >/dev/null 2>&1 stty "$oldstty" echo -e "\nNow continuing...." exit 0 |
Ensure that you restore your old stty settings before exiting. This is probably best achieved by using traps. The following script demostrates these concepts well, and gives the foundations of a Minesweeper-style grid, cursors, and cell-marking.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
#!/bin/ksh # Script demonstrating terminal control - like "minesweeper" # Well, the rudiments of minesweeper anyway - draws a grid, and # uses vi-like cursor commands to control the cursor: # # Key Action # --- ------ # h left # j down # k up # l right # m mark "cell" with an "x" # u unmark current "cell" # ? tentatively mark a cell with a "?" # x exit # # As of yet, has no "gameplay" but controls the cursor reliably and # provides the framework # Reset terminal at exit trap "stty echo icanon; tput clear" 0 trap "stty echo icanon; tput clear; exit 1" 1 2 3 15 # use -icanon to process each char in turn - no need to press return stty -echo -icanon # will be restored at exit tput clear row=1 col=1 # "constants" max_columns=17 max_lines=19 # Draw the main grid i=1 while [ "$i" -le $(( $max_lines + 2 )) ] do if [ $(( $i % 2 )) -eq "0" ]; then echo "| | | | | | | | | |" else echo "-------------------" fi (( i = i + 1 )) done tput cup $row $col # infinite while - key handlers are in here! while : do # read one char from stdin key=$( dd bs=1 count=1 2> /dev/null ) case "$key" in h) #left if [ "$col" -le "1" ]; then col=1 tput cup $row $col else (( col = col - 2 )) tput cup $row $col fi ;; l) #right if [ "$col" -ge "$max_columns" ]; then col=$max_columns tput cup $row $col else (( col = col + 2 )) tput cup $row $col fi ;; k) #up if [ "$row" -le "1" ]; then row=1 tput cup $row $col else (( row = row - 2 )) tput cup $row $col fi ;; j) #down if [ "$row" -ge "$max_lines" ]; then row=$max_lines tput cup $row $col else (( row = row + 2 )) tput cup $row $col fi ;; m) #mark echo -e "x\c\b" ;; u) #unmark echo -e " \c\b" ;; \?) #tentative echo -e "?\c\b" ;; x) #exit tput cup 21 1 echo -e "Do you want to quit $(basename $0)? [y|n] \c" stty echo icanon # user must press return here read response case $response in y|Y|[yY][eE][sS]) exit 0 ;; *) tput cup 21 1 # overwrite previous output echo " " tput cup $row $col stty -echo -icanon #back to it ;; esac esac done exit 99 # should never get here |