本篇文章介绍 bash 的 select 复合命令,该命令能够提供菜单列表给用户进行选择。shell
在 Linux 的 Bash shell 中,能够用 select
复合命令 (compound command) 提供一个菜单列表给用户选择,并根据用户的选择进行相应地处理。查看 man bash 里面对 select 命令的说明以下:bash
select name [ in word ] ; do list ; done
The list of words following "in" is expanded, generating a list of items. The set of expanded words is printed on the standard error, each preceded by a number. If the "in word" is omitted, the positional parameters are printed.The PS3 prompt is then displayed and a line read from the standard input. If the line consists of a number corresponding to one of the displayed words, then the value of name is set to that word. If the line is empty, the words and prompt are displayed again. If EOF is read, the command completes. Any other value read causes name to be set to null. 函数
The line read is saved in the virable REPLY. The list is executed after each selection until a break command is executed. ui
The exit status of select is the exit status of the list command executed in list, or zero if no commands were executed.spa
在 select
命令格式中,[ in word ]
表示 "in word" 是可选参数,用于提供菜单列表,实际输入的时候不须要输入 []
这两个字符。而 name 变量会保存用户选择的菜单项内容,能够获取 name 变量值来查看用户的选择。每次用户输入选择后,会执行 list 指定的命令。翻译
注意:这里的 []
并非 bash 里面的条件判断命令。Bash 有一个 [
条件判断命令,其格式是 [ 参数... ]
,二者格式比较类似,注意区分,不要搞混。code
当没有提供 in word 参数时,select
命令默认使用 in "$@" 参数,也就是传入脚本、或者传入函数的参数列表来做为菜单选项。字符串
select
命令使用 in word 参数来指定菜单列表,不一样的菜单项之间用空格隔开,不要用双引号把整个菜单列表括起来,不然会被当成一个菜单项。input
当用双引号把菜单列表括起来时,整个菜单列表被当成一个选项。若是某个选择项的内容确实要包含空格,就能够单独用双引号把这个选择项的内容括起来,避免该选项该分割成多个选项。it
选择以后,select
命令不会自动退出,而是等待用户继续选择,须要执行 break
命令来退出,也能够按 CTRL-D 来输入EOF进行退出。输入EOF退出后,name 变量值会保持以前的值不变,不会被自动清空。
在 select
命令的内部语句里面,能够用 exit
命令来直接退出整个脚本的执行,从而退出 select
的选择。若是是在函数内调用 select
命令,则能够用 return
命令来直接退出整个函数,从而退出 select
的选择。
假设有一个 testselect.sh
脚本,内容以下:
#!/bin/bash select animal in lion tiger panda flower; do if [ "$animal" = "flower" ]; then echo "Flower is not animal." break else echo "Choose animal is: $animal" fi done echo "++++ Enter new select ++++" select animal in "lion tiger panda"; do echo "Your choose is: $animal" break done
这个脚本的第一个 select
命令定义了四个菜单项:lion,tiger,panda,flower,若是用户选择了 flower 则执行 break
命令退出 select 的选择,不然会打印出用户选择的动物名。
第二个 select
命令用双引号把菜单选项括起来,以便查看加了双引号后的效果。
执行该脚本,输出结果以下:
$ ./testselect.sh 1) lion 2) tiger 3) panda 4) flower #? 1 Choose animal is: lion #? 2 Choose animal is: tiger #? 7 Choose animal is: #? lion Choose animal is: #? 4 Flower is not animal. ++++ Enter new select ++++ 1) lion tiger panda #? 1 Your choose is: lion tiger panda
能够看到,select
命令要经过菜单项前面的数字来选择对应的项,并把对应项的名称赋值给指定的 animal 变量。
输入菜单项自己的字符串并不能选择这个菜单项。输入无效的菜单项编号不会报错。这两种状况都会把 animal 变量值清空。
上面的 #?
是 bash 的 PS3 提示符,咱们能够为 PS3 变量赋值,再执行 select
命令,从而打印自定义的提示信息。举例以下:
$ PS3="Enjoy your choose:> " $ select animal in lion tiger; do echo "Choose: $animal"; break; done 1) lion 2) tiger Enjoy your choose:> 1 Choose: lion
能够看到,为 PS3 变量赋值后,select
命令会打印所赋值的内容,做为提示符。
咱们能够修改 bash 的 IFS 变量值,指定不一样菜单项之间的分割字符,但在使用上有一个注意事项,具体说明以下:
$ IFS=/ $ animal_list="big lion/small tiger" $ select animal in $animal_list; do echo "Choose: $animal"; break; done 1) big lion 2) small tiger #? 1 Choose: big lion $ select animal in big lion/small tiger; do echo "Choose: $animal"; break; done 1) big 2) lion/small 3) tiger #? 2 Choose: lion/small
上面的例子把 IFS 赋值为 /
,而后定义 animal_list 变量,用 /
隔开了 big lion、small tiger 两项,用 select
命令从 animal_list 变量获取菜单选项时,能够看到再也不用空格来隔开选项,而是用 IFS 赋值后的 /
来隔开选项。
可是,当 select
命令不从 animal_list 变量获取菜单选项,而是直接写为 select animal in big lion/small tiger
命令时,它仍是用空格来隔开选项,而不是用 IFS 赋值后的 /
来隔开选项。
缘由在于,IFS 用于 bash 扩展后的单词拆分,使用 $animal_list
获取 animal_list 变量值就是一种扩展,从而发生单词拆分,用 IFS 的值来拆分红几个单词。
直接写为 in big lion/small tiger
没有发生扩展,因此没有使用 IFS 来拆分单词。
查看 man bash 对 IFS 的说明以下:
IFS
The Internal Field Separator that is used for word splitting after expansion and to split lines into words with the read builtin command. The default value is ``<space><tab><newline>''.
翻译成中文就是,IFS (Internal Field Separator) 用于在扩展后进行单词拆分,并使用 read 内置命令将行拆分为单词。
即,要在扩展以后,才会用 IFS 的值来拆分单词。
在经过 IFS 修改 select
命令的菜单项分割字符时,菜单列表须要保存到变量里面,而后在 select
命令里面获取该变量值,进行变量扩展,IFS 才会生效。