什么是shell
Shell是一个命令解释器,就像是包裹在Linux内核外面的一层外壳,在Linux的终端中我们输入命令后计算机的内核并不能直接执行这些命令, 我们都知道计算机执行的是二进制的代码,而Shell的作用就正好是将这些命令解释成计算机可以执行的二进制代码,而内核就可以执行这些二进制代码, 内核可以控制计算机的硬件工作比如:声卡、网卡等计算机硬件。优势在于处理操作系统底层的业务,有大量的linux系统命令为它作支撑,特别是grep awk sed等。 学好 shell编程并实现通过shell脚本自动化管理系统必备基础 1、vi/vim编辑器的熟练使用,ssh终端及“vimrc”的设置等要搞熟练 2、命令基础 3、linux正则表达式及三剑客grep sed awk 4、常见linux网络部署、优化及排错。例:crond nfs rsync inotify lannmp sersync ssh memcache [root@shell ~]# cat /etc/shells /bin/sh/bin/bash/sbin/nologin/bin/dash/bin/tcsh/bin/csh[root@shell ~]# echo $SHELL/bin/bash[root@shell script]# bash --versionGNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu) shell 脚本开发基本规范及习惯 1、开头指定脚本解释器#!/bin/sh或#!/bin/sh 2、版权信息 可以通过~/.vimrc #Date #Author #Mail #Function #Version 3、英语注释 4、以.sh结尾 5、成对符号一次性写全,中括号内侧两边各留空格 6、流程语句一次性写完整 7、注意缩进 使用bash命令参数调试 sh [-nvx] scripts.sh -n 不会执行该脚本,仅查询脚本语法是否有问题,并给出错误提示 -v 在执行脚本时,先将脚本的内容输出到屏幕上然后执行脚本,如果有错误,也会给出错误提示 -x 将执行的脚本内容及输出显示到屏幕上,这个是对调试很游泳的参数 提示 1、同bash命令参数功能 2、开启调试功能通过set -x命令,关闭用set +x 优点:和bash -x相比,set -x可以缩小调试的作用域 脚本中需要调试的代码前后 set -x diff /tmp/a /tmp/b set +x
shell脚本的执行
当shell脚本以非交互式方式运行时,它会先查找系统环境变量(通常是.bachrc .bash_profile /etc/bashrc /etc/profile等), 从该环境变量文件开始执行脚本,当读取ENV文件后,shell才会执行文本中的内容.例如定时任务脚本,调用系统变量建议重新定义,以免这些变量不被加载 shell执行方式 1、sh script-name 或 bash script-name (推荐方式) 2、/path/script-name或 ./script-name 需要加x权限 3、sh
变量
变量简单的说,变量就是用一个固定的字符串(也可能是字母数字等的结合),代替更多复杂的内容,这个内容里面可能还会包含变量和路径,字符串等其他内容,变量的定义是在内存中的。变量类型 环境变量(env,set):用于定义shell运行环境 全局配置地点 /etc/profile /etc/bashrc /etc/profile.d/*.sh /etc/profile =>USER/LOGNAME/MAIL/PATH/HOSTNAME/HISTSIZE/umask/调用/etc/profile.d/*.sh文件 用户配置地点 ~下.bash_profile .bashrc 设置环境变量1、export LANG=en_US 或先定义再export 设置全局环境变量:写入/etc/profile,source生效 取消设置 unset 环境变量名 全局变量的定义 export VAR=value VAR=value;export VAR declare [+/-][option] VAR - 给变量设定类型属性 + 取消变量类型属性 -i 将变量声明为整型 -x 将变量声明为环境变量 -p 显示指定变量被声明的类型 [root@node83 ~]# a=1 [root@node83 ~]# b=2 [root@node83 ~]# c=$a+$b [root@node83 ~]# echo $c 1+2 [root@node83 ~]# declare -x -i d=$a+$b [root@node83 ~]# echo $d 3
局部变量本地变量:在用户当前shell生存周期的脚本中使用1、普通字符串变量定义 变量名=value 变量名='value' 变量名="value"2、变量名 由字母、数字、下划线,以字母开头默认类型都是字符串型 [root@shell ~]# a=111[root@shell ~]# b=222[root@shell ~]# c=333[root@shell ~]# echo "a=$a" =>a=111[root@shell ~]# echo 'b=$b' =>b=$b[root@shell ~]# echo c=$c =>c=333[root@shell ~]# echo c=${c} =>c=333 双引号 会解析双引号中的变量名,转义字符等,命令用反引号。简单的整体连续数字,字符串,路径名可以不加引号单引号 所见即所得反引号 一般用于命令,会被执行( 或 $() ) # echo $(date) Sat Jun 24 07:06:58 CST 2017 # echo `date` Sat Jun 24 07:07:06 CST 2017 以上内容可能不适用sed awk(特殊)两个类似,引用变量用单引号 [root@shell script]# awk 'BEGIN {print "$ETT"}' $ETT [root@shell script]# awk 'BEGIN {print '$ETT'}' 123 [root@shell script]# awk 'BEGIN {print $ETT}'空tar zcvf etc_$(date +%F).tar.gz /etc/hoststar zcvf etc_`date +%F`.tar.gz /etc/hosts注意${WEEK}day若变量和其他字符组成新的变量就必须给变量加上{}[root@shell home]# a=hello [root@shell home]# echo $aworld [root@shell home]# echo $(a)world -bash: a: command not found world [root@shell home]# echo ${a}world helloworld 一道实用linux运维问题的9种shell解答方法!http://oldboy.blog.51cto.com/2561410/760192
位置变量
shell特殊变量(man bash Parametre块)1、位置变量 $0 获取当前执行的shell脚本的文件名,如果执行脚本带路径,那么就包括脚本路径 $n 获取当前执行脚本的第n个参数,如果大于9,用花括号 $# 获取当前执行的shell脚本后面接的参数总个数 $? 返回命令执行状态值 $* 获取当前脚本所有传参的参数 $@ 这个程序的所有参数 "$1" "$2" "$3 "$1" 常用来做case判断
*) echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" exit 2[root@shell ~]# /etc/init.d/crond Usage:/etc/init.d/crond {start|stop|status|restart|condrestart|try-restart|reload|force-reload}[root@shell ~]# /etc/init.d/crond ddUsage: /etc/init.d/crond {start|stop|status|restart|condrestart|try-restart|reload|force-reload}
目录、文件名分离
[root@shell /]# cat /root/script/p.sh dirname $0basename $0[root@shell /]# sh /root/script/p.sh /root/script 脚本路径p.sh 脚本名
$n 获取当前执行脚本的第n个参数,当n=0时表示脚本的文件名,如果大于9用大括号括起来${ 10},参数以空格分开
[root@shell script]# cat p.sh echo $1[root@shell script]# sh p.sh start start[root@shell script]# sh p.sh start stopstart[root@shell script]# sh p.sh "start stop"start stop [root@shell script]# cat p.sh echo $0 $1 $2 $3 $4 $# $*[root@shell script]# sh p.sh 1 2 3p.sh 1 2 3 3 1 2 3
$# 获取当前执行的shell脚本后面接的参数总个数,常用来进行参数控制
[root@shell script]# cat p.sh ==>echo $0 $1 $2 $3 $4 $#[root@shell script]# sh p.sh 1 2 3 ==>p.sh 1 2 3 3[root@shell script]# cat test.sh #!/bin/sh[ $# -ne 2 ] && { echo "$0 ARG1 AGR2" exit 1}echo $0 [root@shell script]# sh test.sh a test.sh ARG1 AGR2[root@shell script]# sh test.sh a btest.sh http://oldboy.blog.51cto.com/2561410/1175971runlevel=$(set -- $(runlevel); eval "echo \$$#" )1)这里的$#就是参数个数为2,即runlevel的结果, 所以\$$#就是$2,即runlevel结果的第二列3[root@G307 ~]# runlevelN 32)eval就是把echo的字符串,当做命令解析! http://oldboy.blog.51cto.com/2561410/1665163 18道Shell高级编程企业实战题及参考答案(一)http://oldboy.blog.51cto.com/2561410/1632876
$? 返回命令执行状态值 在脚本中exit 控制数字,函数中return 返回值给$? 0成功 非0不成功 一般情况可能--> 1或2权限拒绝 1-125失败 126找到命令,但是无权执行,命令找不到127 企业场景返回值用法 1、判断命令或脚本是否执行成功 2、脚本中函数中的exit 及return返回值给$?$* 获取当前脚本所有传参的参数,将所有的参数视为一个字符串,相当于"$1$2$3"... 注意与$#的区别 main $*取所有参数变成一个传给main $@ 这个程序的所有参数 "$1" "$2" "$3" "..." 这是将参数传递给其他程序的最佳方式,因为会保留所有内嵌在每个参数里的任何空白。"$@" "$*" 都要加双引号。 # set -- "I am" super gtms # echo $# 3 # for i in "$*";do echo $i;done I am super gtms # for i in "$@";do echo $i;done I am super gtms 进程状态变量 $$ 当前执行shell进程号(方便以后管理) $! 执行上一次指令的PID $? 获取上一指令的返回值 $_ 在此之前执行的命令或脚本的最后一个参数 # mv a.pid pid.sh # cat pid.sh echo $$ > /tmp/a.pid sleep 300 # sh pid.sh & [2] 1813 # ps -ef | grep pid.sh | grep -v grep root 1813 1645 0 22:28 pts/0 00:00:00 sh pid.sh # cat /tmp/a.pid 1813 # kill `cat /tmp/a.pid`
bash变量子串的常用操作(一般了解即可)
${#string} 返回$string的长度 | # GTMS="HELLO WORLD"# echo $GTMS | wc -L ==>11# echo ${#GTMS} ==>11# expr length "$GTMS" ==>11[root@shell script]# cat p.sh for n in I am gtms linux welcome to our training.do if [ ${#n} -lt 6 ] then echo $n fidone输出小于6个字符的单词 |
${string:position} 在$string中,从位置position之后开始提取子串 position从0开始 | [root@shell script]# echo $GTMSHELLO WORLD[root@shell script]# echo ${GTMS:2}LLO WORLD |
${string:position:length} 在$string中,从位置position之后开始提取长度为length的子串 | [root@shell script]# echo $GTMS HELLO WORLD[root@shell script]# echo ${GTMS:2:3}LLO |
${string#substring} 从$string开头开始删除最短匹配substring的子串 | # GTMS=abcABC123ABCabc# echo ${GTMS#a*C} /"*是通配符"123ABCabc /前面的被匹配删除 |
${string##substring} 从$string开头开始删除最长匹配substring的子串 | [root@shell script]# GTMS=abcABC123ABCabc[root@shell script]# echo ${GTMS##a*c} ====删完了 |
${string%substring} 从$string结尾开始删除最短匹配substring的子串 | [root@shell script]# echo $GTMS abcABC123ABCabc[root@shell script]# echo ${GTMS%C*c} abcABC123AB |
${string%%substring} 从$string结尾开始删除最长匹配substring的子串 | [root@shell script]# echo $GTMS abcABC123ABCabc[root@shell script]# echo ${GTMS%%C*c}abcAB |
${string/pattern/replace} 使用replace来替换第一个匹配到的substring | # echo $OLDBOYI am oldboy oldboy oldgirl# echo ${OLDBOY/oldboy/youngboy} I am youngboy oldboy oldgirl# echo ${OLDBOY/#I*oldboy/youngboy}youngboy oldgirl# echo ${OLDBOY/%old*girl/youngboy}I am youngboy |
${string/#pattern/replace} 如果$string前缀匹配substring,就用replace来替换substring | |
${string/%pattern/replace} 如果$string后缀匹配substring,就用replace来替换substring |
变量替换(了解即可)
${parameter:-word} Use Default Values. If parameter is unset or null, the expan-sion of word is substituted. Otherwise, the value of parameter is substituted.当变量未定义时,返回word内容,否则返回变量的值 | [root@shell script]# result=${test:-UNSET}[root@shell script]# echo $test ==>空,因为test变量未定义[root@shell script]# echo $result ===>UNSET |
${parameter:=word} Assign Default Values. If parameter is unset or null,theexpansion of word is assigned to parameter.The value of param-eter is then substituted. Positional parameters and special parameters may not be assigned to in this way.变量未定义时,word赋给变量 | [root@shell script]# test=ok[root@shell script]# result=${test:-UNSET}[root@shell script]# echo $result ===>okrm -rf ${path:-/tmp} 防止path值为空,从根删除 |
${parameter:?word} Display Error if Null or Unset. If parameter is null or unset,the expansion of word (or a message to that effect if word isnot present) is writtento the standard error and the shell, ifit is not interactive, exits. Otherwise, the value of parameter is substituted. | |
${parameter:+word} Use Alternate Value. If parameter is null or unset, nothing issubstituted, otherwise the expansion of word is substituted. |
变量计算常见命令 (()) let expr bc(小数) $[] 其他都是整数
变量的数值运算 id++ id-- variable post-increment and post-decrement ++id --id variable pre-increment and pre-decrement - + unary一元 minus and plus ! ~ logical and bitwise negation非 ** exponentiation幂 * / % multiplication, division, remainder余数 + - addition, subtraction << >> left and right bitwise shifts <= >= < >comparison == != equality and inequality & bitwise AND ^ bitwise exclusive OR 位的异或 | bitwise OR && logical AND || logical ORexpr?expr:exprconditional operator = *= /= %= += -= <<= >>= &= ^= |= assignment expr1 , expr2 comma 1、(()) 只支持整数 (效率高,建议使用)
[root@shell script]# ((a=1+2**3-4%3)) [root@shell script]# echo $a ===>8[root@shell script]# ((a=a+1)) [root@shell script]# echo $a ===>9[root@shell script]# echo $((1+2**3-4%3)) ===>8----------------------------[root@shell script]# ((a=1+2**3-4%3))[root@shell script]# echo $a 8[root@shell script]# echo $((a++)) 8[root@shell script]# echo $((a++)) 9[root@shell script]# echo $((a--)) 10[root@shell script]# echo $((a--)) 9[root@shell script]# echo $a 8----------------------------[root@shell script]# ((a=1+2**3-4%3)) [root@shell script]# echo $((++a)) 9 [root@shell script]# echo $((++a)) 10[root@shell script]# echo $((--a)) 9[root@shell script]# echo $((--a)) 8[root@shell script]# echo $a 8===================================================== [root@shell home]# cat test.sh #!/bin/bash a=6 b=2 echo "a-b =$(($a-$b))" echo "a+b =$(($a+$b))" echo "a*b =$(($a*$b))" echo "a/b =$(($a/$b))" echo "a**b =$(($a**$b))" echo "a%b =$(($a%$b))" ======================================================一个坑[root@shell script]# i=1[root@shell script]# i=i+1[root@shell script]# echo $i ===> i+1[root@shell script]# i=1[root@shell script]# i=$((i+1))[root@shell script]# echo $i ===>2 [root@node83 ~]# a=$(( 1 && 1)) [root@node83 ~]# echo $a 1
2、let (整数计算) [root@shell script]# i=1 [root@shell script]# let i=i+1 ==>((i=i+1))效率高 [root@shell script]# echo $i ==> 2 3、expr (整数) 计算符号左右空格 *(乘号)用\屏蔽
[root@shell script]# i=0[root@shell script]# i=`expr $i+1` [root@shell script]# echo $i 0+1[root@shell script]# i=0[root@shell script]# i=`expr $i + 1` [root@shell script]# echo $i 1 SYNOPSIS expr EXPRESSION expr OPTION Print the value of EXPRESSION to standard output. A blank line below separates increasing precedence groups. EXPRESSION may be: ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2 ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0 ARG1 < ARG2 ARG1 is less than ARG2 ARG1 <= ARG2 ARG1 is less than or equal to ARG2 ARG1 = ARG2 ARG1 is equal to ARG2 ARG1 != ARG2 ARG1 is unequal to ARG2 ARG1 >= ARG2 ARG1 is greater than or equal to ARG2 ARG1 > ARG2 ARG1 is greater than ARG2 ARG1 + ARG2 arithmetic sum of ARG1 and ARG2 ARG1 - ARG2 arithmetic difference of ARG1 and ARG2 ARG1 * ARG2 arithmetic product of ARG1 and ARG2 ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2 ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2 STRING : REGEXP anchored pattern match of REGEXP in STRING 字符串匹配 /usr/bin/ssh-copy-id脚本中,扩展名判断 if [ -n "$2" ]; then if expr "$1" : ".*\.pub" > /dev/null ; then ID_FILE="$1" else ID_FILE="$1.pub" fi match STRING REGEXP same as STRING : REGEXP substr STRING POS LENGTH substring of STRING, POS counted from 1 index STRING CHARS index in STRING where any CHARS is found, or 0 length STRING length of STRING #expr length "$char"计算字符串长度 利用expr进行整数判断 while true do read -p "pls input an int:" a expr $a + 1 &> /dev/null [ $? -eq 0 ] && echo "int " || echo "not int" done [root@shell ~]# chars=`seq -s" " 100` [root@shell ~]# time for i in $(seq 11111);do count=${#chars};done (效率高) real 0m0.038s user 0m0.037s sys 0m0.000s [root@shell ~]# time for i in $(seq 11111);do count=`echo expr lenth "${chars}"`;done real 0m3.585s user 0m0.180s sys 0m0.867s [root@shell ~]# time for i in $(seq 11111);do count=`echo ${chars}| wc -L`;done; real 0m12.755s user 0m0.348s sys 0m0.870s
4、bc 可以整数也可以小数(直接bc回车计算)
[root@shell script]# i=1.3 [root@shell script]# i=`echo $i+1|bc` [root@shell script]# echo $i ===>2.3 [root@shell script]# echo "1.1-1" | bc .1 [root@shell script]# echo "5.5 5.6" | awk '{print ($2-$1)}' ===>0.1 [root@shell script]# echo "`seq -s '+' 10`="$((`seq -s "+" 10`)) 1+2+3+4+5+6+7+8+9+10=55 [root@shell script]# echo `seq -s '+' 10`=`seq -s "+" 10| bc` 1+2+3+4+5+6+7+8+9+10=55 [root@shell script]# echo `seq -s '+' 10`=`seq -s " + " 10| xargs expr` 1+2+3+4+5+6+7+8+9+10=55 [root@shell script]# echo {1..9}"+" 10=`echo {1..9}"+" 10 |bc`|sed 's# ##g' 1+2+3+4+5+6+7+8+9+10=55
5、$[]的用法 [root@shell script]# i=1 [root@shell script]# i=$[ i + 3] [root@shell script]# echo $i 4
条件测试与比较
语法形式 方法一 test <> 方法二 [空格 <测试表达式> 空格] 建议此种 方法三 [[ <测试表达式> ]] 可以使用通配符进行模式匹配 文件测试表达式man test 常用的文件测试操作符(变量加双引号) -d FILE FILE exists and is a directory 文件存在且为目录则真 -e FILE FILE exists 文件存在即为真 -f FILE FILE exists and is a regular file 文件存在且为普通文件则真 -L FILE FILE exists and is a symbolic link (same as -h) -s FILE FILE exists and has a size greater than zero 文件存在且大小不为0则真 FILE1 -nt FILE2 FILE1 is newer (modification date) than FILE2 FILE1 -ef FILE2 FILE1 and FILE2 have the same device and inode numbers FILE1 -ot FILE2 FILE1 is older than FILE2 -z STRING the length of STRING is zero -b FILE FILE exists and is block special -c FILE FILE exists and is character special -g FILE FILE exists and is set-group-ID -G FILE FILE exists and is owned by the effective group ID -h FILE FILE exists and is a symbolic link (same as -L) -k FILE FILE exists and has its sticky bit set -O FILE FILE exists and is owned by the effective user ID -p FILE FILE exists and is a named pipe -r FILE FILE exists and read permission is granted -w FILE FILE exists and write permission is granted -x FILE FILE exists and execute (or search) permission is granted -S FILE FILE exists and is a socket -t FD file descriptor FD is opened on a terminal -u FILE FILE exists and its set-user-ID bit is set test条件测试语法及实例 man test 语法格式 test -f (文件存在且为普通文件则表达式成立) [root@shell ~]# test -f /etc/hosts &&echo 1 ==>1 [root@shell ~]# test -f /etc/hosts &&echo 1 || echo 0 ==>1 [root@shell ~]# test -f /etc/host &&echo 1 || echo 0 ==>0 [root@shell ~]# test ! -f /etc/hosts &&echo 1 || echo 0 ==>0 [root@shell ~]# test ! -f /etc/host &&echo 1 || echo 0 ==>1 [root@shell ~]# test -z "oldboy" && echo 1 || echo 0 ==>0 -z:string长度为0 [root@shell ~]# test -z "" && echo 1 || echo 0 ==>1 [root@shell ~]# [ -z "oldboy" ] && echo 1 || echo 0 ==>0 [root@shell ~]# [ -z ""] && echo 1 || echo 0 ==>1 [root@shell ~]# [[ -e "/etc/hosts" || -f /etc/hosts ]] && echo 1 || echo 0 ==>1 [root@shell ~]# [ -e "/etc/hosts" || -f /etc/hosts ] && echo 1 || echo 0 -bash: [: missing `]' -bash: -f: command not found 0 [root@shell ~]# [ -e "/etc/hosts" -o -f /etc/hosts ] && echo 1 || echo 0 1 逻辑连接符 测试表达式> 测试表达式>
[] | (()) | [[]] |
-a | && | and |
-o | || | or |
! | ! | not |
[root@shell ~]# f1=/etc/rc.local;f2=/etc/services [root@shell ~]# [ -f "$f1" -a -f "$f2" ] && echo 1 || echo 0 ==>1 [root@shell ~]# [ -f "$f1" -a -f "$f3" ] && echo 1 || echo 0 ==>0 [root@shell ~]# [ -f "$f1" -o -f "$f3" ] && echo 1 || echo 0 ==>1 # vi /etc/init.d/rsyslog if [ -f /etc/sysconfig/$prog ] ; then . /etc/sysconfig/$prog fi start() { [ -x $exec ] || exit 5 # vi /etc/init.d/nfs [ -x /usr/sbin/rpc.nfsd ] || exit 5 [ -x /usr/sbin/rpc.mountd ] || exit 5 [ -x /usr/sbin/exportfs ] || exit 5 文件测试表达式写法案例 [root@shell ~]# [ -f /etc/hosts ] &&{ echo 1;echo 2;echo 3; } || echo "bye" 1 2 3 ==>相当于if语句 if [ express ] then 命令1 命令2 命令3 fi 条件不成立时执行命令(用于脚本中,写在一行中,需要分号) [ 3 -ne 3] || { echo "aaaaaaa" echo "bbbbbbb" exit 1 } 字符串测试表达式(必须注意双引号,比较符号两端空格)man test -n "STRING" the length of STRING is nonzero 长度不为0为真 -z "STRING" the length of STRING is zero 长度0为真 "STRING1" = "STRING2" the strings are equal "STRING1" != "STRING2" the strings are not equal 实例 [root@shell ~]# sed -n '30,31p' /etc/init.d/network # Check that networking is up. [ "${NETWORKING}" = "no" ] && exit 6 [root@shell ~]# [ -n "abc" ] && echo 1 || echo 0 ==>1 [root@shell ~]# [ -z "abc" ] && echo 1 || echo 0 ==>0 [root@shell ~]# [ "abc" == "abcd" ] && echo 1 || echo 0 ==>0 [root@shell ~]# [ "abc" == "abc" ] && echo 1 || echo 0 ==>1 [root@shell ~]# test1=abc [root@shell ~]# test2=abd [root@shell ~]# [ "${#test1}" = "${#test2}" ] &&echo 1 || echo 0 (取长度) ==>1 [root@shell ~]# vim /etc/init.d/nfs [ -z "$MOUNTD_NFS_V2" ] && MOUNTD_NFS_V2=default [ -z "$MOUNTD_NFS_V3" ] && MOUNTD_NFS_V3=default [ -z "$RPCNFSDCOUNT" ] && RPCNFSDCOUNT=8 # Set the ports lockd should listen on if [ -n "$LOCKD_TCPPORT" -o -n "$LOCKD_UDPPORT" ]; then [ -x /sbin/modprobe ] && /sbin/modprobe lockd $LOCKDARG [ -n "$LOCKD_TCPPORT" ] && \ /sbin/sysctl -w fs.nfs.nlm_tcpport=$LOCKD_TCPPORT >/dev/null 2>&1 [ -n "$LOCKD_UDPPORT" ] && \ /sbin/sysctl -w fs.nfs.nlm_udpport=$LOCKD_UDPPORT >/dev/null 2>&1 fi if [ -n "$RQUOTAD" -a "$RQUOTAD" != "no" ]; then echo -n $"Starting NFS quotas:
整数测试表达式 man test
整数二元比较操作符在[]中及test使用 在(())及[[]]中使用
[]及test | (()) | [[]] |
-eq equal | == 或= | |
-ne not equal | != | |
-gt greate than | > | |
-ge greate than | >= | |
-lt less than | < | |
-le leass equal | <= |
小结:整数比较推荐下面用法 [ $num1 -eq $num2 ] ==>注意空格和比较符号 (($num1>$num2)) ==>无需空格,常规数学比较符号 [root@shell ~]# [ 2 -gt 3 ] && echo 1 || echo 0 ==>0 [root@shell ~]# [ 2 > 3 ] && echo 1 || echo 0 ==>1 错了 [root@shell ~]# [[ 2 > 3 ]] && echo 1 || echo 0 ==>0 [root@shell ~]# a1=10;a2=13 [root@shell ~]# [ $a1 -eq $a2 ] && echo 1 || echo 0 ==>0 整数比较方法推荐 [ $num1 -eq $num2 ] (($num1 > $unm2)) 利用条件表达式完成,通过传参两个参数,比较两个整数大小 #!/bin/sh [ $# -ne 2 ] &&{ echo "USAGE:"$0" num1 num2" exit 1 } #judge int expr $1 + $2 &>/dev/null [ $? -ne 0 ]&&{ echo "pls input two nums:" exit 2 } =========================== expr $1 + 1 &>/dev/null RETVAL1=$? expr $2 + 1 &>/dev/null RETVAL2=$? [ $RETVAL1 -ne 0 ] && { echo "the first num is not int,pls input two nums:" exit 2 } [ $RETVAL2 -ne 0 ] && { echo "the second num is not int,pls input two nums:" exit 3 } =========================== #compare [ $1 -lt $2 ] &&{ echo "$1<$2" exit 0 } [ $1 -eq $2 ] &&{ echo "$1=$2" exit 0 } [ $1 -gt $2 ] &&{ echo "$1>$2" exit 0 }
变量输入及菜单打印
变量输入的3种方式 1、定义 a=1 2、传参方式 $1 3、read交互式读入[root@shell ~]# read -p "pls input:" a pls input:aaaa[root@shell ~]# echo $aaaaa [root@shell ~]##!/bin.shread -p "pls input a character:" aecho "your input is: $a"综合实例:打印选择菜单,一键安装web服务
cat方式cat >>menu.txt <<EOF 1. 2. 3.EOF | echo方式 echo ' 1. 2. 3.‘ |
[root@shell ~]# cat menu.sh cat <
if条件句语法
单分支结构
if [condition] then 指令fi | if [condition];then 指令fi | if [ -f "$file1" ];then echo 1;fi | [ -f "$file1"]&& echo 1 |
双分支结构
if [ condition ] then 指令 else 指令fi | if [ -f "$file1" ];then echo 1;else echo 0;fi | [ -f "$file1"]&& echo 1 || echo 0 |
陷阱:过滤进程时,脚本名不要带有被过滤的进程名:g/$2/s//$b/g 替代多分支结构if [ condition1 ] then 指令1 elif [ condition2 ] then 指令2 else 指令3fi
[root@shell ~]# cat comparebigsmall.sh #!/bin/bashif [ $# -ne 2 ]; then echo "USAGE:$0 arg1 arg2" exit 2fiexpr $1 + 1 &>/dev/nullRETVAL1=$?expr $2 + 1 &>/dev/nullRETVAL2=$?if [ $RETVAL1 -ne 0 -a $RETVAL2 -ne 0 ];then echo "please input two int" exit 3fiif [ $RETVAL1 -ne 0 ];then echo "the first num is not int,pls input again" exit 4fiif [ $RETVAL2 -ne 0 ];then echo "the second num is not int,pls input again" exit 5fiif [ $1 -lt $2 ];then echo "$1<$2" exitelif [ $1 -eq $2 ];then echo "$1=$2" exitelse echo "$1>$2" exitfi | [root@shell ~]# cat comparebigsmall-chuancan.sh#!/bin/bash#read 方式read -p "pls input two num:" a b[ -z "$a" ]||[ -z "$b" ] && { echo "pls input two num again" exit 1}#传参方式a=$1b=$2if [ $# -ne 2 ]; then echo "USAGE:$0 arg1 arg2" exit 2fiexpr $a + 1 &>/dev/nullRETVAL1=$?expr $b + 1 &>/dev/nullRETVAL2=$?if [ $RETVAL1 -ne 0 -a $RETVAL2 -ne 0 ];then echo "please input two int" exit 3fiif [ $RETVAL1 -ne 0 ];then echo "the first num is not int,pls input again" exit 4fiif [ $RETVAL2 -ne 0 ];then echo "the second num is not int,pls input again" exit 5fiif [ $a -lt $b ];then echo "$a<$b" exitelif [ $a -eq $b ];then echo "$a=$b" exitelse echo "$a>$b" exitfi |
shell函数
优势: 1、把相同的程序段定义成函数,减少整个程序代码量 3、增加可读性、易读性 3、实现程序功能模块化shell函数语法 简单语法格式 函数名(){ 指令集 return n } 规范语法格式 function 函数名(){ 指令集 return n } 函数的调用 1、直接执行函数名(不带括号) [root@shell ~]# cat functest.sh boy(){ #先定义 echo "i am a boy" } function girl(){ #定义 echo "i am a girl" } boy #后执行 girl 函数的引用 . functest.sh或source functest.sh 写程序时定义 bin fun log conf目录
2、带传参函数 函数名 参数1 参数2 后接参数 shell位置参数可用,除$0仍然是父脚本的 父脚参数临时被函数参数所掩盖或隐藏,函数完成时,恢复 函数中return与shell中exit类似 函数中exit会退出整个shell 函数的参数变量是在函数体里面的定义
函数传参 | 脚本传参传给函数传参 |
[root@shell ~]# cat functest.sh who() { echo "i am $1" } who gtms ==>gtms即$1 [root@shell ~]# sh functest.sh i am gtms | [root@shell ~]# cat functest.sh who() { echo "i am $1" } who $1 [root@shell ~]# sh functest.sh gtms i am gtms |
实例
[root@shell ~]# cat checkweb.sh #!/bin/bash check_url() { wget -T 10 --spider -t 2 $1 &>/dev/null if [ $? -eq 0 ] then echo "$1 is ok" else echo "$1 is not ok" fi } check_url $1 [root@shell ~]# sh checkweb.sh www.baidu.com www.baidu.com is ok | [root@shell ~]# cat checkweb.sh #!/bin/bashusage() { echo "USAGE:$0 arg" exit 1}check_url() { curl -s $1 &>/dev/nullif [ $? -eq 0 ] then echo "$1 is ok" else echo "$1 is not ok"fi}main(){ if [ $# -ne 1 ] then usage fi check_url $1}main $*[root@shell ~]# sh checkweb.sh www.baidu.com www.baidu.com is ok |
case结构语句
case语法 case 值 in 模式1) command1 command2 command3 ;; 模式2|模式3) command1 command2 command3 ;; *) command1 command2 command3 ;; esac 使用示例 #!/bin/bash read -p "pls input a number in 10: " num case $num in 1) echo "number is 1" ;; 2) echo "number is 2" ;; [3-9]) echo "number is $num" ;; *) echo "the number you input is not in 10" exit; esac case语句小结: 1、case语句就相当于多分支的if语句。 case语句优势更规范、易读。 2、case语句适合变量的值少,且为固定的数字或字符串集合。(1,2,3)或(start,stop,restart)。 3、系统服务启动脚本传参的判断多用case语句。多参考rpcbind/nfs/crond脚本。 小结: 1)所有的case语句都可以用if实现,但是case更规范清晰一些, 2)case一般适合于服务的启动脚本。 3)case的变量的值如果已知固定的start/stop/restart元素的时候比较适合一些。
已知nginx管理命令为:启动:/usr/local/nginx/sbin/nginx停止:/usr/local/nginx/sbin/nginx -s stop重新加载:/usr/locall/nginx/sbin/nginx -s reload请用case脚本模拟nginx服务启动关闭:/etc/init.d/nginx {start|stop|restart|reload} 并实现可通过chkconfig管理。[root@test88 script]# cat nginx.sh (未完工)#!/bin/sh[ -f /etc/init.d/functions ] && . /etc/init.d/functionsnginx="/usr/local/nginx/sbin/nginx"prog="nginx"RETVAL=0start() { if [ ! -f /var/lock/subsys/$prog ];then $nginx && RETVAL=$? else echo "nginx is running" exit fi if [ $RETVAL -eq 0 ];then touch /var/lock/subsys/$prog action "starting nginx" /bin/true else action "starting nginx" /bin/false fi return $RETVAL}stop() { $nginx -s stop && RETVAL=$? if [ $RETVAL -eq 0 ];then rm -f /var/lock/subsys/$prog action "nginx is stopped" /bin/true else action "nginx is stopped" /bin/false fi return $RETVAL}reload() {if [ -f /var/lock/subsys/$prog ];then $nginx reload && RETVAL=$? else echo "nginx is stopped" exit fi$nginx -s reload return $RETVAL}case "$1" in start) start ;; stop) stop ;; restart) stop start ;; reload) reload ;; *) echo "USAGE :$0 {start|stop|restart|reload}"esacexit $RETVALcp nginx.sh /etc/init.d/nginxchmod +x nginx.shnginx脚本中添加#!/bin/sh下#chkconfig: 2345 54 65chkconfig --add nginxchconfig nginx onchkconfig --list
case结构语句
while条件语法 while 条件 do 指令 done [root@shell ~]# cat while-1.sh #!/bin/bash while true 表示条件永远为真 do uptime sleep 2 done ==>[root@shell ~]# while true;do uptime;sleep 2;done [root@shell ~]# cat sum100.sh #!/bin/sh sum=0 i=1 while [ $i -le 100 ] do ((sum=sum+i)) ((i++)) done echo $sum ==>[root@shell ~]# echo $(( (1+100) * 100/2 )) while按行读文件的方式
方式1 | 方式2 | 方式3 |
exec <filesum=0while read linedo cmddone | cat ${file_path} | while read linedo cmddone | while read linedo cmddone<FILE |
使用案例:读入文件,计算总流量 [root@shell ~]# cat sum-while.sh #!/bin/sh sum=0 while read line do value= `echo $line | awk '{print $10}'` expr $value + 1 &>/dev/null [ $? -ne 0 ] && continue ((sum+=value)) done
for循环结构
for循环结构语法 for 变量名 in 取值列表 do 指令 donec语言型for循环结构 for ((expr1;expr2;expr3)) do 指令 doneseq -w 100102~~~~~#直接列出变量列表所有元素,打印5 4 3 2 1for num in 5 4 3 2 1 do echo 10.0.0.$num donefor ip in 192.168.0.1 192.168.0.2 { 1..5} `seq 10` do echo $ip done 分库备份 [root@shell ~]# cat database_bak.sh #!/bin/sh MYUSER=root MYPASS=rootabcd MYCMD="mysql -u$MYUSER -p$MYPASS" MYDUMP="mysqldump -u$MYUSER -p$MYPASS" BACKPATH="/bak/database/$(date +%F)" [ ! -d $BACKPATH ] && mkdir -p $BACKPATH for dbname in `$MYCMD -e 'show databases;' | sed '1d' | grep -v "_schema"` do $MYDUMP -B -x --events $dbname | gzip > $BACKPATH/${dbname}.sql.gz if [ $? -eq 0 ];then echo "$dbname">>$BACKPATH/${dbname}.log fi done 分表备份 root@shell ~]# cat database_tablebak.sh #!/bin/sh MYUSER=root MYPASS=rootabcd MYCMD="mysql -u$MYUSER -p$MYPASS" MYDUMP="mysqldump -u$MYUSER -p$MYPASS" BACKPATH="/bak/database/$(date +%F)" [ ! -d $BACKPATH ] && mkdir -p $BACKPATH for dbname in `$MYCMD -e 'show databases;' | sed '1d' | grep -v "_schema"` do for tname in `$MYCMD -e "show tables from $dbname;" | sed '1d'` do $MYDUMP -x --events $dbname $tname | gzip > $BACKPATH/${dbname}_${tname}.sql.gz if [ $? -eq 0 ];then echo "$dbname_$tname">>$BACKPATH/${dbname}.log fi done done
break n 跳出循环的层数,省略n表示跳出整个循环continue n n表示退到n层继续循环,省略表示跳过本次循环exit n 退出shell程序,n为返回值return n 用于函数返回值
颜色的使用示例
#!/bin/shRED_COLOR='\E[1;31m'GREEN_COLOR='\E[1;32m'YELLOW_COLOR='\E[1;33m'BLUE_COLOR='\E[1;34m'RES='\E[0m'echo -e "$RED_COLOR boy $RES"echo -e "$YELLOW_COLOR girl $RES" man手册查看颜色man console_codes
前景色 echo -e "\033[30m 黑色字hello world \033[0m" echo -e "\033[31m 红色字hello world \033[0m" echo -e "\033[32m 绿色字hello world \033[0m" echo -e "\033[33m 黄色字hello world \033[0m" echo -e "\033[34m 蓝色字hello world \033[0m" echo -e "\033[35m 紫色字hello world \033[0m" echo -e "\033[36m 天蓝字hello world \033[0m" ==等于# echo -e "\e[36m 天蓝字hello world \e[0m" echo -e "\033[37m 白色字hello world \033[0m" 背景色 echo -e "\033[40;37m 黑底白字 welcome to shanghai\033[0m" echo -e "\033[41;37m 红底白字 welcome to shanghai\033[0m" echo -e "\033[42;37m 绿底白字 welcome to shanghai\033[0m" echo -e "\033[43;37m 黄底白字 welcome to shanghai\033[0m" echo -e "\033[44;37m 蓝底白字 welcome to shanghai\033[0m" echo -e "\033[45;37m 紫底白字 welcome to shanghai\033[0m" echo -e "\033[46;37m 天蓝白字 welcome to shanghai\033[0m" echo -e "\033[47;30m 白底黑字 welcome to shanghai\033[0m" 字背景颜色范围:40----49 40:黑 41:深红 42:绿 43:黄色 44:蓝色 45:紫色 46:深绿 47:白色 字颜色:30-----------39 30m:黑 31m:红 32m:绿 33m:黄 34m:蓝色 35m:紫色 36m:深绿 37m:白色 ==ANSI控制码的说明 \33[0m 关闭所有属性 \33[1m 设置高亮度 \33[4m 下划线 \33[5m 闪烁 \33[7m 反显 \33[8m 消隐 \33[30m -- \33[37m 设置前景色 \33[40m -- \33[47m 设置背景色 \33[nA 光标上移n行 \33[nB 光标下移n行 \33[nC 光标右移n行 \33[nD 光标左移n行 \33[y;xH设置光标位置 \33[2J 清屏 \33[K 清除从光标到行尾的内容 \33[s 保存光标位置 \33[u 恢复光标位置 \33[?25l 隐藏光标 \33[?25h 显示光标 使用示例:将颜色定义为函数 [root@shell home]# cat colour.sh add_colour() { RED_COLOR='\E[1;31m' GREEN_COLOR='\E[1;32m' YELLOW_COLOR='\E[1;33m' BLUE_COLOR='\E[1;34m' RES='\E[0m' case "$1" in red|RED) echo -e "$RED_COLOR $2 $RES" ;; green|GREEN) echo -e "$GREEN_COLOR $2 $RES" ;; yellow|YELLOW) echo -e "$YELLOW_COLOR $2 $RES" ;; blue|BLUE) echo -e "$BLUE_COLOR $2 $RES" ;; *) echo "please use:{red|green|yellow|blue}" exit esac } add_colour red "hello world" add_colour green "hello world" 批量创建用户 [root@shell ~]# cat adduser.sh for n in `seq -w 10` do useradd gtms$n pass=`echo $RANDOM | md5sum| cut -c 2-9` echo $pass | passwd --stdin gtms$n echo -e "gtms$n \t $pass" >>/tmp/user done 批量创建密码 [root@shell ~]# useradd test1 [root@shell ~]# useradd test2 [root@shell ~]# useradd test3 [root@shell ~]# cat user.pass test1:123456 test2:123456 test3:123456 [root@shell ~]# chpasswd
随机数
RANDOM随机数echo $RANDOM 随机数0-32767echo "$RANDOM gtms" | md5sum 通过openssl产生随机数openssl rand -base64 8openssl rand -base64 10通过时间函数获得随机数date +%s%N通过uuid[root@shell ~]# cat /proc/sys/kernel/random/uuidf69ee9b7-fe0c-4173-b07c-5a881f7a739a[root@shell ~]# cat /proc/sys/kernel/random/uuidcbd8b0e9-24cc-44dd-9d96-dec53d7624a4使用mkpasswd 生成随机数yum install expect -y[root@test88 script]# mkpasswd -l 8yWG8u\6a[root@test88 script]# mkpasswd -l 9vq18MapV-对于长短不一的随机数 使用md5sum | cut -c 1-9 格式化