Linux-Shell函数和图形菜单 背景 此前讲述的都是单一脚本运行所有的命令,其中不乏一些重复的代码,如果是少量的都还好,但是一旦面对大批量重复的代码时就会显得很啰嗦,所以此处就讲到在Linux中使用Shell函数
,从而降低重复代码的编写工作。
同样,为了便于更加友好的使用Shell脚本,还可以创建图形菜单,本文也对该内容进行了简要描述。
全文笔记来源于《Linux命令行与Shell编程大全》
内容 Shell函数 与其它语言类似,在shell中也可以编写函数,它的定义方式如下:
1 2 3 4 5 6 7 8 9 # 方式1:借助关键字funcation funcation name { commands } # 方式2:直接定义 name() { commands }
比如:
1 2 3 4 5 6 7 8 funcation print { echo "who you are" } # 也可以 print() { echo "who you are" }
当我们需要调用函数的时候,则直接写函数名即可,比如:
需要注意的地方有两处:第一,函数必须先定义才能够调用,否则报错;第二,函数名必须唯一,否则后重名的函数会重写掉前面同名的函数。
那么作为一个函数,其目的是为了处理单一功能的需求,有时则需要返回数据。在shell中,shell函数同样也可以返回数据,返回数据有三种方式:
直接获取默认的退出状态码:$?
。该状态码记录的是函数中最后一条命令执行的返回状态码,不能够代表函数内部没有错误
1 2 3 4 5 6 function name { echo "this is a file" } name echo "function run is ok? $?"
利用return
获取自定的退出状态码:return num
。它记录的也只是最后一条命令执行的返回状态码,不代表函数内部没有错误。
1 2 3 4 5 6 7 8 9 10 function name { read -p "please enter a number" value return $[ $value*2 ] } name # 使用return 切记:函数一调用完就要直接获取其退出状态码,否则$?记录的都是上一条命令的返回状态码 echo "function run is ok? $?" # 使用return 语句还有一点需要注意:return 的值必须在0~255,这个是命令的返回状态码限制的
返回指定的数据:echo data
。前两种方式返回的内容太渣,我们必须要返回带有艺术的东西时,就使用echo
吧!!!
1 2 3 4 5 6 7 8 9 10 # !/bin/bash function name { read -p "please enter a number:" value value=$[ $value*2 ] echo $value } # 使用命令互换接住函数的返回值 data=$( name ) echo $data
这种方式有一点需要注意:在函数中,凡是直接输出到STDOUT
上的,都是被当作返回值返回,所以如果不是我们需要的数据,则在函数中利用重定向将不需要的数据过滤。
函数中,还有一个概念就是参数传递,在shell的函数中,参数是通过命令行的方式进行传递的,格式如下:
1 2 # 切记函数名与参数必须位于同一行中 name var1 var2 var3 ...
在函数中,如果需要引用到传递的参数,则可以使用$1~$9
获取,$0
表示该函数名,同样$#表示传递进去的参数总数,$@
和$*
获取所有传递进去的参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function name { if [ $# -eq 2] then result=$[ $1*$2] elif [ $# -lt 2 ] then result="var is less than 2" else result=0 for i in $@ do result=$[ $result+i] done fi echo $reult } # 此处切记,如果是从命令行传递过来的参数是无法直接被引用到函数中的 res=$( name 1 2 ) echo "this result is $res"
其实在shell中,也分全局变量和局部变量,在shell脚本的主体部分定义的变量都是全局变量,在整个shell中(包括函数中)都可以访问,而在函数内部定义的变量则只能在函数内部进行访问。
1 2 3 4 5 6 7 function name { var=1 echo $[ $var+$var2 ] } var2=2 data=$( name ) echo "the result is $data"
同样还可以借助关键字local
在函数内部标识变量为局部变量
1 2 3 4 5 6 7 8 9 function name { # 看这里 local var var=1 echo $[ $var+$var2 ] } var2=2 data=$( name ) echo "the result is $data"
之前讲过在shell中,也有列表类型的变量,如果我们想将列表类型的变量传递到函数中,则仅仅通过变量名传递是不行的,需要在函数中依次获取列表变量的值,组成一个新的列表后才能正常的使用。
1 2 3 4 5 6 7 8 function name { local newarray newarray=( $@ ) # --> 注意此处 echo "${newarray[*]}" # --> 此处就可以将新的数组进行返回 } array=( 1 2 3 4 5 6 7 8 9 ) data=$( name ${array[*]} ) # --> 注意此处 echo "the result is $data"
讲到函数,就会说到一个概念:递归调用
,这个在shell中也可以实现,具体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 function mi { if [ $1 -gt 2 ] then local temp=$( mi $[ $1-1] ) echo $[ $temp * $1 ] else echo 1 fi } read -p " please enter a number: " num data=$( mi $num ) echo "the result is $data"
上面定义函数的方式是在一个shell脚本之中进行定义,当有多个脚本需要使用同一个函数的时候,此时该方法就不适用了。所以就引入了一个概念:公共库函数
,即将函数部分独立成一个单独的shell脚本文件,然后在需要使用到函数的部分引入这个公共库函数文件即可。
定义公共库函数的脚本内容就全是function
的函数,可以忽视,但是如果需要在shell脚本中引用公共库函数,则就需要借助命令source
:
1 2 3 4 5 6 7 8 9 10 11 # source 后面添加公共库函数文件的路径 source ./file read -p " please enter a number: " num data=$( mi $num ) echo "the result is $data" # source 又名点操作符,所以可以用.代替,因此上述内容又可简写为 . ./file read -p " please enter a number: " num data=$( mi $num ) echo "the result is $data"
其实,函数不仅仅支持在shell脚本中,在命令行中也可以定义函数并调用
1 2 3 4 5 # 格式都是一样的,只是需要换行的部分则需要用;代替, 并且每一行命令的结束都要尾部追加; function name {local tmp; tmp=$[ $1*$2 ]; echo $tmp;} # 使用的时候,则直接函数名+参数 name 1 2
当然这样的方式很不友好,所以对于一些确实需要在命令行下运行的命令,一般都直接写到.bashrc
文件中,因为这个文件在shell运行的时候就会自动加载,这样就避免了重复的劳作。
图形菜单 在图形界面中,程序运行都会有一个菜单桌面,比较友好,其实在shell中,也可以编辑图形菜单,本文记录两种方式,都是基于命令行的。最简单的就是文本菜单了,菜单的选择借助于case
命令了,页面的信息清理借助于clear
命令比如:
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 # 创建菜单函数 function menu { clear echo # -e:可以将\t等转义字符进行输出出来 echo -e "1.\t查看磁盘的使用情况" echo -e "2.\t查看当前目录下的文件占用情况" echo -e "3.\t查看当前登录的用户" echo -e "4.\t退出当前的Shell程序" echo -en "\n\n\t\tPlease enter your choose: " read -n 1 option # --> 这里读取的option变量可以在主函数中使用,我也不是很明白 } function disk { clear echo -e "云服务器的磁盘空间使用如下:\n" df -h } function amount { clear echo -e "当前目录:$(pwd)使用情况如下:\n" du -h } function loguer { clear echo -e "当前系统下登录的用户有如下:\n" who } while [ 1 ] # --> 相当于while true do menu case $option in 1 ) disk;; 2 ) amount;; 3 ) loguer;; 4 ) clear break;; * ) clear echo "your choose is error";; esac echo -e "\n\n\t\tEnter any key to continue" read -en 1 line done
上面的方式在使用的时候就已经比较友好了,但是在创建菜单的时候还是浪费较多的内容在编辑菜单上,其实shell提供了一个易于上手的菜单编辑命令:select ... in ...
,格式如下:
1 2 3 4 5 6 # PS3是select ... in ...中特殊的环境变量,用于输入的提示符 PS3="Enter your choose:" select option in list do commands done
比如将上面的例子改写:
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 function disk { clear echo -e "云服务器的磁盘空间使用如下:\n" df -h } function amount { clear echo -e "当前目录:$(pwd)使用情况如下:\n" du -h } function loguser { clear echo -e "当前系统下登录的用户有如下:\n" who } PS3="Enter your option:" select option in "查看磁盘的使用情况" "查看当前目录下的文件占用情况" "查看当前登录的用户" "退出当前的Shell程序" do case $option in "查看磁盘的使用情况" ) disk;; "查看当前目录下的文件占用情况" ) amount;; "查看当前登录的用户" ) loguser;; "退出当前的Shell程序" ) clear break;; * ) echo -e "Your choose is error";; esac done
上述的内容只是基于文本模式的,和图形桌面是一点关系也没有,不过也已经非常的友好了。但是还有另外一个库:dialog
,它应该算是命令行窗口下的图形菜单了,首先可能我们需要进行安装“:
然后就是dialog
使用的格式:
1 2 3 4 # 它可以在任意的位置运行哈 # widget:dialog的部件名 # parameters:定义部件窗口的大小及部件需要的文本 dialog --widget parameters
关于dialog
的运行,有需要说明之处:
dialog
有两种输出方式:命令退出状态码
、STDERR
对于选择类部件,如果用户选择的是yes/ok
,则退出状态码为0
;若选择的是Cancel/no
,则退出状态码为1
;
对于有返回值的部件,则会将返回值直接输出到STDERR
上,此时则需要用户进行重定向获取。
其次是部件及参数的n多种类:https://www.cnblogs.com/klb561/p/9043142.html
举个例子:
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 # !/bin/bash # using dialog to create a menu temp=$( mktemp -t test.XXXXXX ) temp2=$( mktemp -t test2.XXXXXX ) function diskspace { df -k > $temp dialog --textbox $temp 20 60 # 这个就很灵性了,因为$temp是个文件,它却能直接读取文件的内容 } function whoseon { who > $temp dialog --textbox $temp 20 50 } function memusage { cat /proc/meminfo > $temp dialog --textbox $temp 20 50 } function showwho { whoami > $temp dialog --textbox $temp 20 50 } function cpu { daedecode > $temp dialog --textbox $temp 20 50 } while [ 1 ] do dialog --menu "Sys Admin Menu" 20 30 10 1 "Display disk space" 2 "Display users" 3 "Display memory usage" 4 "Display whoami" 5 "Display cpu info" 0 "Exit" 2> $temp2 if [ $? -eq 1 ] then break fi selection=$(cat $temp2) case $selection in 1 ) diskspace ;; 2 ) whoseon ;; 3 ) memusage ;; 4 ) showwho ;; 5 ) cpu ;; 0 ) break ;; * ) dialog --msgbox "Sorry, invalid selection" 10 30;; esac done rm -f $temp 2> /dev/null rm -f $temp2 2> /dev/null