├── README-zh.md ├── README.md ├── msh.etc ├── msh.fun └── msh.sh /README-zh.md: -------------------------------------------------------------------------------- 1 | 只需要通过简单的配置就可以生成一个shell菜单,包含的功能有: 2 | 3 | 1. 指定菜单是否必须执行 4 | 2. 显示菜单执行的结果,成功的会显示一个✓,失败的会显示一个✕ 5 | 3. 指定当前菜单所依赖的其它菜单,只有依赖的菜单执行成功后,才会允许执行当前菜单 6 | 4. 菜单具有层级,一个菜单下可以包含子菜单,可执行菜单的后面会显示一个 * 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # msh 2 | [中文介绍](README-zh.md) 3 | An easy to use shell menu, the view and function are seperated. The view is in msh.etc file, and the function is in msh.fun file. What's showing in the menu is easy to change by only editing the msh.etc file. 4 | # Installing and Running 5 | Get the code: 6 | ``` 7 | git clone https://github.com/qihaiyan/msh.git 8 | ``` 9 | Then run: 10 | ``` 11 | cd msh 12 | ``` 13 | ``` 14 | sh msh.sh 15 | ``` 16 | or 17 | ``` 18 | chmod u+x msh.sh 19 | ./msh.sh 20 | ``` 21 | 22 | ![msh](https://cloud.githubusercontent.com/assets/5896784/19466245/370baa86-953c-11e6-8049-f99c7a4bf4b4.jpeg) 23 | 24 | # msh.etc 25 | The definitation of the menu with msh.etc is very simple: 26 | ``` 27 | PROMPT Input your choice 28 | BANNER banner banner 29 | 1 mysubmenu submenu banner1 30 | 1-1 menufun1-1 fun101 0 31 | 1-2 menufun1-2 fun102 1 1-1 32 | 2 menufun2 fun2 1 1-1 33 | ``` 34 | There are 5 fields separated with TAB char in this file for the menu contect. 35 | 36 | 1. Menu Id 37 | 2. Menu Name 38 | 3. Function Name, if this menu has sub menus, the function name must be hard written to `submenu`. 39 | 4. Redo Flag, used to define if this menu can be executed repeatly. 40 | 5. Depend Menu Id, current menu is ready only if the depended menu has succeed . 41 | 42 | The fun101, fun102 ... is the function name we defined in msh.fun, 43 | when we choose a menu id, the correspond function will be called. 44 | # msh.fun 45 | msh.fun is just a shell file, we can define functions refered in msh.etc . 46 | ``` bash 47 | banner(){ 48 | echo " TOP-MENU" 49 | echo " =============================" 50 | } 51 | banner1(){ 52 | echo " SUB-MENU" 53 | echo " =============================" 54 | } 55 | fun101(){ 56 | echo "function 101 executed." 57 | } 58 | fun102(){ 59 | date 60 | } 61 | fun2(){ 62 | echo "function 2 executed." 63 | return 2 64 | } 65 | ``` 66 | -------------------------------------------------------------------------------- /msh.etc: -------------------------------------------------------------------------------- 1 | PROMPT Input your choice 2 | BANNER banner banner 3 | 1 mysubmenu submenu banner1 4 | 1-1 menufun1-1 fun101 0 5 | 1-2 menufun1-2 fun102 1 1-1 6 | 2 menufun2 fun2 0 1-1 7 | -------------------------------------------------------------------------------- /msh.fun: -------------------------------------------------------------------------------- 1 | banner(){ 2 | echo " TOP-MENU" 3 | echo " =============================" 4 | } 5 | banner1(){ 6 | echo " SUB-MENU" 7 | echo " =============================" 8 | } 9 | fun101(){ 10 | echo "function 101 executed." 11 | } 12 | fun102(){ 13 | date 14 | } 15 | fun2(){ 16 | echo "function 2 executed." 17 | return 2 18 | } 19 | -------------------------------------------------------------------------------- /msh.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | resfile="./msh.etc" 4 | funfile="./msh.fun" 5 | lockfile="./.msh" 6 | currlevel="1" 7 | current= 8 | currmenu="0" 9 | choice= 10 | title= 11 | cmd= 12 | redoflg= 13 | dependflg= 14 | status= 15 | errmsg= 16 | blank=" " 17 | 18 | . $funfile 19 | 20 | __showmenu(){ 21 | clear 22 | 23 | prompt=`awk -F"\t" '$1 ~ /^PROMPT$/{print $2}' $resfile` 24 | 25 | if [ $1 = "0" ] 26 | then 27 | banner=`awk -F"\t" '$1 ~ /^BANNER$/{print $2}' $resfile` 28 | expression="\$1 ~ /^.\$/" 29 | else 30 | subbanner=`awk -F"\t" -v MENUID="$1" '{if($1==MENUID && $3=="submenu"){print $4}}' $resfile` 31 | if [ ! "X$subbanner" = "X" ] 32 | then 33 | banner=$subbanner 34 | fi 35 | expression="\$1 ~ /^$1-.\$/" 36 | fi 37 | 38 | $banner 39 | 40 | awk -F"\t" -v BLANK="$blank" -v ETCFILE="$lockfile" -v MENUID="$1" \ 41 | 'BEGIN{\ 42 | while ( getline tracelog < ETCFILE == 1 ){\ 43 | split(tracelog,menuid);\ 44 | idstatus[menuid[1]]=menuid[2];\ 45 | }\ 46 | }\ 47 | { \ 48 | if ('"$expression"'){ \ 49 | if ($3!="submenu"){\ 50 | sign="*";\ 51 | }\ 52 | else{\ 53 | sign="";\ 54 | }\ 55 | if (idstatus[$1]=="OK"){\ 56 | _status=" √";\ 57 | }\ 58 | else if (idstatus[$1]==""){\ 59 | _status="";\ 60 | }\ 61 | else{\ 62 | _status=" ✕";\ 63 | }\ 64 | print BLANK $1" "$2 sign _status; \ 65 | lastid=$1;\ 66 | } \ 67 | }\ 68 | END{\ 69 | if ( idstatus[MENUID]=="" && idstatus[lastid]=="OK" ){\ 70 | print MENUID " OK" >> ETCFILE; 71 | }\ 72 | }' $resfile 73 | 74 | printf "${blank}q quit\n" 75 | printf " $prompt: " 76 | } 77 | 78 | __getfunc(){ 79 | __i=0 80 | title= 81 | cmd= 82 | redoflg= 83 | dependflg= 84 | 85 | __line=`awk -F"\t" '{ if ($1 ~ /^'"$current"'$/)print $0 }' $resfile` 86 | for __p in ${__line// /} 87 | do 88 | __i=`expr $__i + 1` 89 | if [ $__i -eq 2 ] 90 | then 91 | title=$__p 92 | elif [ $__i -eq 3 ] 93 | then 94 | cmd=$__p 95 | elif [ $__i -eq 4 ] 96 | then 97 | redoflg=$__p 98 | elif [ $__i -eq 5 ] 99 | then 100 | dependflg=$__p 101 | fi 102 | done 103 | } 104 | 105 | submenu(){ 106 | currlevel=`expr $currlevel + 1` 107 | currmenu="$current" 108 | } 109 | 110 | __upmenu(){ 111 | 112 | currlevel=`expr $currlevel - 1` 113 | 114 | if [ "$currlevel" = "1" ] 115 | then 116 | currmenu="0" 117 | else 118 | __len=`expr $currlevel \* 2 - 3` 119 | currmenu=`printf "%."$__len"s" $currmenu` 120 | fi 121 | } 122 | 123 | __getcmd(){ 124 | 125 | read choice 126 | 127 | if [ "X$choice" = "Xq" ] 128 | then 129 | if [ "X$currlevel" = "X1" ] 130 | then 131 | cmd="QUIT" 132 | else 133 | cmd="__upmenu" 134 | fi 135 | else 136 | if [ "X$currlevel" = "X1" ] 137 | then 138 | current=$choice 139 | else 140 | __len=`expr $currlevel \* 2 - 3` 141 | current=`printf "%."$__len"s" $current`"-$choice" 142 | fi 143 | __getfunc 144 | fi 145 | } 146 | 147 | __cmdcheck(){ 148 | 149 | if [ "X$cmd" = "Xsubmenu" -o "X$cmd" = "X__upmenu" ] 150 | then 151 | return 1 152 | fi 153 | 154 | __statuslist=`awk -F"\t" -v depend=$dependflg '{if ($1 == depend)print $2}' $lockfile` 155 | __status=`echo $__statuslist | awk '{print $NF}'` 156 | 157 | if [ ! "X$dependflg" = "X" -a ! "X$__status" = "XOK" ] 158 | then 159 | errmsg=`echo "this step depends on [$dependflg] which is not executed or failed."` 160 | return 0 161 | fi 162 | 163 | __statuslist=`awk -F"\t" -v current=$current '{if ($1 == current)print $2}' $lockfile` 164 | __status=`echo $__statuslist | awk '{print $NF}'` 165 | 166 | if [ "X$redoflg" = "X0" -a "X$__status" = "XOK" ] 167 | then 168 | errmsg=`echo "this step can't be executed repeatly."` 169 | return 0 170 | fi 171 | 172 | return 1 173 | } 174 | 175 | __exec(){ 176 | if [ ! "X$cmd" = "X" ] 177 | then 178 | __cmdcheck 179 | if [ $? -eq 1 ] 180 | then 181 | $cmd 182 | status=$? 183 | if [ $status -eq 0 ] 184 | then 185 | status="OK" 186 | fi 187 | 188 | if [ ! "X$cmd" = "Xsubmenu" -a ! "X$cmd" = "X__upmenu" ] 189 | then 190 | printf "%s %s %s\n" $current $status `date +%Y/%m/%d-%H:%M:%S` >> $lockfile 191 | echo "press ENTER to return." 192 | read __a 193 | fi 194 | else 195 | echo "$errmsg" 196 | read __a 197 | fi 198 | fi 199 | } 200 | 201 | if [ ! -f $lockfile ] 202 | then 203 | > $lockfile 204 | fi 205 | 206 | while true 207 | do 208 | 209 | __showmenu $currmenu 210 | 211 | __getcmd 212 | 213 | if [ "X$cmd" = "XQUIT" ] 214 | then 215 | printf "${blank}quit(y/n)?" 216 | read __ok 217 | if [ "X$__ok" = "Xy" ] 218 | then 219 | break 220 | else 221 | continue 222 | fi 223 | elif [ "X$cmd" = "XE" ] 224 | then 225 | continue 226 | else 227 | __exec 228 | fi 229 | 230 | done 231 | 232 | rm -f $lockfile 233 | --------------------------------------------------------------------------------