1. Shell脚本基础
把一系列的Linux命名放在一个文件中, 下面为shell的脚本的格式:
#!/bin/bash shell脚本的起始符号,指定脚本解释器//Linux命令集Cd #shell注释..bash_profileDatewho
Shell脚本中反引号`允许将shell命令的输出赋值给变量。Linux下出来输入和输出重定向还有内联输入重定向,下面是其示例样式:
wc << EOF> test string 1> test string 2> test string 3> EOF
1.1 数学计算
Bash中可以使用如下方式将数学运算包括起来:((数学表达式))。
Bash默认只支持整数运算,要进行浮点数运算需要使用bc(bash内建的计算器: 变量=echo "bc选项;数学表达式"| bc
第二种方式是使用内联重定向,格式见示例:
#!/bin/bash#shell中的数学计算和浮点数运算解决方案var1=100var2=50var3=45var4=$[$var1*($var2-$var3)] #常规整数运算echo "整数运算的输出结果:$var4"var5=`echo "scale=4;$var1/$var3"|bc` #echo 格式的浮点数运算echo "浮点数echo解决方案的输出结果:$var5"var6=`bc << EOF #内联重定向的浮点数运算scale=4$var1/$var3EOF`echo "浮点数内联重定向解决方案的输出结果:$var6"
1.2 条件分支语句
Shell中if-else命令和python等脚本的类似,但是if或elif的条件是命令执行的状态而不是一个等式的真假,这是要注意的地方。下面是if条件分支的示例:
#!/bin/bash#if-then-else条件分支示例testuser=badtestif grep $testuser /etc/passwd;thenecho "该用户主目录下的文件有:"ls -a /home/$testuser/.b*#若还有条件可以用elifelseecho "用户$testuser在系统中不存在"fi
If只能用命令执行结果作为判断依据,如果要进行数的比较怎么办?这时应该使用test命令。Test命令可以进行数字,字符串,文件等多种比较,为真则返回状态码0,从而进行if操作。在if语句中可以用test 条件语句或[条件语句]来使用test比较。
| 数值比 | |
|---|---|
| N1 -eq n2 | N1,n2是否相等 |
| N1 -ge n2 | N1是否大于等于n2 |
| N1 -gt n2 | N1是否大于n2 |
| N1 -le n2 | N1是否小于等于n2 |
| N1 -lt n2 | N1是否小于n2 |
| N1 -ne n2 | N1是否不等于n2 |
| 字符串比较 | |
| Str1 = str2 | 是否相等(!=) |
| Str1 < str2 | Str1是否比str2小(>),注意需要\进行转义操作 |
| -n str1 | Str1的长度是否非0 |
| -z str1 | Str1长度是否为0 |
| 文件比较 | |
| -d file | File是否存在且是一个目录 |
| -e file | File是否存在 |
| -f file | File是否存在且是一个文件 |
| -r file | File是否存在可读(-w,-x) |
| -s file | File是否存在且非空 |
| -O file | File是否存在并属于当前用户所有 |
| -G file | File是否存在并默认组和当前用户相同 |
| File1 -nt file2 | File1是否比file2新 |
| File1 -ot file2 | File1是否比file2旧 |
除了使用test表示条件还有两种bash新增的扩展:
- ((表达式)):数学比较,可以使用标准的常见比较字符和运算,并且不需要进行转义
- [[表达式]]:字符串比较,使用test命令的比较,并且还可以只用正则表达式
1.3 循环命令
第一种循环是for循环,它有一个in列表,循环会依次取列表中的值进行操作。In列表可以直接添加,以空格为分割(可以IFS=$‘ ‘指定别的分隔符),也可以从变量读取或者从命令读取(需要’ ‘)。
for var in Alabama Alaska Arizona Arkansas California Colorado "new York";do echo the next state is $vardone"C语言风格的for循环for (( i=1;i<=10;i++));doecho "the next number is $i"done
While循环是另一种常用的循环方式,除了使用test进行条件判断,还可以指定多个命令,最后一个命令的状态决定循环是否继续:
while [ $var1 -gt 0 ];doecho $var1var1=$[ $var1-1 ]done"while可以有多个命令,一个命令一行,最后一个测试命令状态决定循环是否继续var2=10while echo $var2 [ $var2 -gt 0 ];doecho this is inside the loopvar2=$[$var2-1]done
Until命令和while格式相同,工作方式相反,until要求指定一个状态非0的命令或比较,只有条件非0才会执行循环内容。
在done之后加一个输出重定向>可以将循环的输出重定向到一个文件中,也可以用管道|作为参数传递给另外一个shell命令。
1.4 处理用户输入
第一种用户输入是使用位置参数,位置参数变量是标准的数字,#表示参数的个数,$1-{10}。位置参数可以加入任意多个命令行参数。另外,@会存储所有的数据,不同的是@作为多个独立的单词保存可以进行循环遍历。
#!/bin/bash#位置参数,允许用户输入,在运行脚本同时指定参数total=$[ $1*$2 ]echo "第一个参数为:$1" echo "第二个参数为:$2"echo "两者的乘积为:$total" name=`basename $0` #basename命令可以返回程序名而去掉路径echo "程序名为:$name"
Shift命令默认会将每个参数减一,即$2变为$1,$1删除,可以用作参数遍历。Shift还可以指定移动的位数,默认为1,可以改变为其他值:shift 2。
当脚本同时有option和参数,并且选项又带有参数,这时候分离选项和参数就很重要。在shell中getopts可以将这一过程简单化。Getopts的格式为:getopts optstring vatiable。Optstring为有效的选项字母,若选项带有参数则在字母后加冒号;若要去掉错误信息则在optstring前加冒号。OPTIND全局变量保存参数列表中getopts正在处理的参数的位置。下面是示例:
while getopts :ab:c opt;docase "$opt" ina) echo "执行a选项操作";;b) echo "执行b选项操作,并有参数$OPTARG";;#获取选项参数c) echo "执行c选项操作";; #有多个选项参数同样访问$OPTARG,会自动区分*) echo "未知的选项:$opt";;esac done shift $[ $OPTIND -1 ] #shift和$OPTIND结合来移动参数count=1 for param in $@;doecho "位置参数 $count:$param"count=$[$count+1]done
除了在执行脚本是指定参数和选项,同样可以在脚本执行过程中获取用户的输入。Read 变量命令可以从键盘或文件接受输入,并存入一个标准变量。下面是read命令在脚本中常见用法:
read -p "输入你的名字:" name #-p为打印输入提示read -p "输入你的英文名:" first last #接受多个输入read -t 5 -p "输入一个名字:" name2 #-t 指定超时时间(秒)read -n 1 -p "继续吗?[Y/N]" answer #-n指定接受的字符个数read -s -p "输入你的密码:" passwd #隐藏输入内容#read从文件读入count=1 cat readfile | while read line;doecho "行数$count:$line"count=$[ $count+1]done
1.5 数据呈现
在脚本中重定向输出有两种:临时重定向和永久重定向。
- 临时重定向:
Echo “this is an error message” >&2 **#**直接将要重定向的内容定向到对应文件描述数字 - 永久重定向:
exec 1>file #将标准输出重定向到文件,会对所有标准输出起作用
永久重定向后,有时候我们需要将输出再变为原来的输出,可以使用下面方法:自定义一个文件描述符指向原来输出,当需要变为原来输出时只要再指向自定义的即可。同样重定向输入也是这样:
"恢复输出:exec 3>&1 exec 1>file exec 1>&3exec 3>&- #关闭文件描述符"恢复输入:exec 4<&0 exec 0<fileexec 0<&4 Exec 4>&-
如果要阻止命令输出或清空文件内容,可以将输出或文件内容重定向到/dev/null件,null的任何数据都不会保存,相当于被丢弃了。
Linux系统的/tmp目录可以存放临时文件,系统可以在启动时自动清理。下面介绍临时文件的创建使用。
tempfile=`mktemp test16.XXXXXX` #创建临时文件,-t 在/tmp下建立文件;-d 建立目录exec 3>$tempfile #自定义输出到临时文件echo "this script writes to temp file $tempfile"echo "this is the first line">&3 exec 3>&echo "Done creating temp file.The contents are:"cat $tempfile #查看临时文件内容 rm -f $tempfile #删除临时文件
2. 高级shell脚本
2.1 脚本控制
捕捉信号:Trap command signals命令在脚本中捕捉信号,脚本受到trap中列出的信号,trap会阻止shell脚本执行它,而是执行command命令。当command为-时,则表示移除后面的信号。下面是trap的示例:
trap "echo 'sorry! I have trapped Ctrl-C'" SIGINT SIGTERM
后台运行脚本:在运行脚本时后面加上&符号可以后台执行,shell可以进行其他操作或者运行多个后台作业。
非终端运行脚本:nohup ./脚本 &。后台运行,即使终端退出也继续执行,输出内容存放在nohup.out文件中。
作业控制:Jobs:列出当前分配给shell的作业,带+的为默认的作业。Bg 作业号:重启停止的作业,后台执行。Fg 作业号:重启作业,前台执行。
优先级调整:脚本启动默认优先级是0(-20~20)。Nice -n 10 ./脚本:nice命令启动脚本时指定优先级。Renice 10 p 进程号:调整正在运行的脚本的优先级。
定时运行作业:at -f 脚本名 time:在特定时间执行脚本,脚本的输出会提交到用户的e-mail,可以用mial命令查看。Atq :列出作业队列在等待的作业。Atrm 作业号:删除等待中的作业。Min hour dayofmonth month dayogweek command:使用cron时间表定时执行command命令。
另外在/etc下存在cron的目录,可以将需要定时执行的脚本复制到相应的cron目录中。
2.2 shell函数
Shell脚本中使用function关键字定义函数,使用echo返回值。给函数传递参数和给脚本传递参数一样,使用$n来获取参数,但是函数不能直接获取脚本参数,要传递给函数:
function addem {if [ $# -eq 0 ] || [ $# -gt 2 ];thenecho -1elif [ $# -eq 1 ];thenecho $[ $1 + $1 ]elseecho $[ $1 + $2]fi}echo -n "10加15等于:" value=`addem 10 15` #传递参数给函数,并接受函数返回值echo $valueecho -n "只输入一个数10:"value=`addem 10` echo $valueecho -n "不输入参数:"value=`addem`echo $value
函数也可以传递数组参数,但是需要将数组变了分解后再作为函数参数使用,不然只能得到数组的第一个值,方法如下:
function array {local newarray #local表示该变量只能在函数内使用newarray=`echo "$@"`echo "The new array value is:${newarray[*]}" #返回数组值}myarray=(1 2 3 4 5)echo "The original array is ${myarray[*]}"array ${myarray[*]} #传递数组参数
Shell还支持函数库文件,把常用的shell函数放在同一个文件中作为库函数,需要使用时通过source命令(即点操作符)倒入库文件引用函数。
. ./funcLab #source命令,注意库文件路径value1=10 value2=5result1=`addem $value1 $value2`echo "相加:$result1"
2.3 图形化脚本
Shell最简单的是文本菜单,可以使用echo显示各个选择项,也可以使用select自动生成菜单,然后在case语句中判断选择执行不同的函数和命令:
select option in "Display disk space" "Display logged on users" "Display memory usage" "Exit program";do case $option in"Exit program") break;;"Display disk space") diskspace;;"Display logged on users") whoseon;;"Display memory usage") menusage;;*) clearecho "sorry,wrong selection";; esac done
Dialog包可以在文本环境用ANSI转义控制字符来创建标准的窗口对话框。下面演示如何在shell脚本中使用dialog。Dialog的标准格式为:dialog --控件名称 控件参数。下面是常用的几个控件,其他可以在dialog帮助中查看:
dialog --title Msgbox --msgbox "this is a msgbox" 10 20 #消息对话框dialog --title Yesno --yesno "this is a yesno" 10 20 #yes/no对话框#输入对话框,注意输入会输出到stderr,需要重定向来获取dialog --title inputBox --inputbox "Enter your name:" 10 20 dialog --title textbox --textbox /etc/passwd 15 45 #文本框,显示大量文本#menu,创建窗口菜单,选项同样输出到stderrdialog --title menu --menu "Sys Admin menu" 20 30 10 1 "Display disk space" 2 "Display users" 3 "exit program"dialog --title fselect --fselect $HOME/ 10 50 #fselect,浏览文件并选择
除了使用基本的dialog包,在gnome和KDE桌面环境下有各自的扩展dialog包:zenity和Kdialog,下面简单介绍zenity。Zenity和dialog不太一样,它许多控件类型都用命令行上其他选项定义,而不是将它们包括在选项的参数中,具体的控件和它需要的参数选项可以在help中查看。
zenity --list --radiolist --title "SYs Admin Menu" --column "Select" --column "MenuItem" FALSE "Display disk space" FALSE "Display users" FALSE "Display memory usage" FALSE "Exit program" >$temp2 #列表框zenity --info --text "Sorry,invalid selection" #提示框zenity --text-info --title "Disk space" --filename=$temp --width 750 --height 10 #文本框
Zenity的默认输出是stdout,和dialog不同。
