Shell脚本编写可选择菜单

使用diary.sh写博客已经有近两年了,对于菜单一直没有细致的设计,这主要是因为对Shell理解的不够透彻。今天实现了真正意义上的可选择菜单,实现原理是:不再使用clear每次按键后清屏,改用指定行输出来输出菜单!这样就可以避免每按一次键就闪烁一次的不好体验,同时对于一屏的信息,当只有一二行发生变化时,整体上看很流畅:

运行截图展示

可选择菜单

可选择菜单支持vim的快捷键,即j向下,k向上,同时J向下一页,K向上一页.

shell源代码

可选择菜单shell源码
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
DY_FILES=($(ls /home/$USER/))  #设置用来列表输出的数组, 此处可以改为您自己的路径
DY_SET_SIZE(){
TTY_H=$(stty size|awk '{print $1}')
let TTY_H-=2
TTY_W=$(stty size|awk '{print $2}')
}
DY_LINE="-"
DY_MKLINE(){
l=0
while [ $l -lt $TTY_W ]; do
echo -n "$DY_LINE"
let l+=1
done
}
NEO_FORMAT=7 # 高亮行的格式,可以自行修改
# 参数: 输出行,行号,数组号, 高亮行号
NEO_PRINT(){
if [[ $3 -eq $4 ]]; then
echo -e "\033[$1;1H\033[?25l\033[${NEO_FORMAT}m[$2] ${NEO_ARR[$3-1]}\033[0m\033[K\033"
else
echo -e "\033[$1;1H\033[?25l[$2] ${NEO_ARR[$3-1]}\033[K"
fi
}
# 参数:$1 起始点 $2 列表长度 $3 高亮行
NEO_LIA(){
k=0 ; i=$1
while [[ $k -lt $TTY_H ]]; do
let k+=1
if [ $i -lt -$2 ]; then
let i=$i+$2
fi
let i+=1
if [ $i -gt $2 ]; then
let i=$i-$2
fi
if [ $i -lt 1 ]; then
let j=$i+$2
else
j=$i
fi
NEO_PRINT $k $j $i $3
done
}
NEO_SELECT(){
echo -ne "\r\033[${NEO_FORMAT}m[s] 选择\033[0m "; echo -ne "\033[K" ; read TLS_SNum
expr "$TLS_SNum" + 0 &> /dev/null
if [ $? -eq 0 ]; then
if [ $TLS_SNum -lt $1 -o $TLS_SNum -gt $2 ]; then
echo "编号超出范围,请重新选择编号!"
exit
else
NEO_OUT_H=$TLS_SNum
fi
else
echo "输入非数字,请重新输入编号!"
exit
fi
}
# 参数: 输出行,光标所在行
NEO_MENUE(){
let r=$1+2
if [ $NEO_SEL_ON -eq 1 ]; then
echo -ne "\033[$r;1H[j] 向下 [k] 向上 [s] 选择 [q] 退出 \033[${NEO_FORMAT}m$2\033[0m\033[K\033[?25h"
else
echo -ne "\033[$r;1H[j] 向下 [k] 向上 [q] 退出 \033[${NEO_FORMAT}m$2\033[0m\033[K\033[?25h"
fi
}
NEO_LISA(){
DY_SET_SIZE
p=$1 ; let q=$p+$TTY_H; m=$3
if [ $q -gt "${#NEO_ARR[*]}" ]; then
let q=$q-"${#NEO_ARR[*]}"
let p=$p-"${#NEO_ARR[*]}"
let m=$m-"${#NEO_ARR[*]}"
fi
if [ $p -le "-${#NEO_ARR[*]}" ]; then
let q=$q+"${#NEO_ARR[*]}"
let p=$p+"${#NEO_ARR[*]}"
let m=$m+"${#NEO_ARR[*]}"
fi
NEO_LIA $p $2 $m
DY_MKLINE
if [ $m -le 0 ]; then
let NEO_CURRENT=$m+"${#NEO_ARR[*]}"
else
NEO_CURRENT=$m
fi
NEO_MENUE "$TTY_H" "$NEO_CURRENT"
read -s -n 1 ListDo
case "$ListDo" in
j)
let m+=1
if [ $m -gt $q ]; then
let p+=$TTY_H
fi
NEO_LISA $p $2 $m
;;
J)
let p+=$TTY_H
let m=$p+1
NEO_LISA $p $2 $m
;;
k)
let m-=1
if [ $m -le $p ]; then
let p-=$TTY_H
fi
NEO_LISA $p $2 $m
;;
K)
let m=$p
let p-=$TTY_H
NEO_LISA $p $2 $m
;;
"")
if [ $NEO_SEL_ON -eq 1 ]; then
NEO_OUT_H=$m
else
exit
fi
;;
s)
if [ $NEO_SEL_ON -eq 1 ]; then
NEO_SELECT $p $q
else
NEO_LISA $p $2 $m
fi
;;
q)
exit
;;
esac
}
NEO_LIB(){
k=0 ; i=$1
while [[ $k -lt $2 ]]; do
let k+=1
if [ $i -lt -$2 ]; then
let i=$i+$2
fi
let i+=1
if [ $i -gt $2 ]; then
let i=$i-$2
fi
if [ $i -lt 1 ]; then
let j=$i+$2
else
j=$i
fi
NEO_PRINT $k $j $i $3
done
}
NEO_LISB(){
DY_SET_SIZE
p=$1 ; q=$2 ; m=$3
if [ $m -gt $q ]; then
let m=$p+1
fi
if [ $m -le $p ]; then
m=$q
fi
NEO_LIB $p $2 $m
DY_MKLINE
NEO_MENUE $q $m
read -s -n 1 ListDo
case "$ListDo" in
j)
let m+=1
NEO_LISB $p $2 $m
;;
k)
let m-=1
NEO_LISB $p $2 $m
;;
s)
if [ $NEO_SEL_ON -eq 1 ]; then
NEO_SELECT $p $q
else
NEO_LISB $p $2 $m
fi
;;
"")
if [ $NEO_SEL_ON -eq 1 ]; then
NEO_OUT_H=$m
else
exit
fi
;;
q)
exit
;;
esac
}
# 参数:列出的数组, 1开启选择,0关闭选择
NEO_LIST(){
NEO_ARR=($1) ; NEO_SEL_ON=$2
clear
DY_SET_SIZE
if [ "${#NEO_ARR[*]}" -gt $TTY_H ]; then
NEO_LISA 0 "${#NEO_ARR[*]}" 1
else
NEO_LISB 0 "${#NEO_ARR[*]}" 1
fi
echo $NEO_OUT_H # 目标是获得光标所在的行
}
NEO_LIST "${DY_FILES[*]}" 1 # 测试程序命令