shell编程
什么是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 --version GNU 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<script-name 或 cat script-name | bash
4、source script-name或 . script-name 个人理解为source能将脚本中定义的变量带到当前shell
防止脚本执行中断的方法
sh while.sh &
nohup /server/scripts/uptime &
screen 保持会话 ,总结此命令
======================================== # cat test.sh
user1=`whoami`
user2=linux
# sh test.sh
# echo $user1
空
# echo $user2
空
# source test.sh
# echo $user1
root
# echo $user2
linux
理解为source能将脚本中的变量带到当前shell环境,而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/hosts tar 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 dd Usage: /etc/init.d/crond {start|stop|status|restart|condrestart|try-restart|reload|force-reload}
目录、文件名分离
[root@shell /]# cat /root/script/p.sh dirname $0 basename $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 stop start [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 3 p.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 b test.sh
http://oldboy.blog.51cto.com/2561410/1175971 runlevel=$(set -- $(runlevel); eval "echo \$$#" ) 1)这里的$#就是参数个数为2,即runlevel的结果, 所以\$$#就是$2,即runlevel结果的第二列3 [root@G307 ~]# runlevel N 3 2)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 fi done 输出小于6个字符的单词 |
${string:position} 在$string中,从位置position之后开始提取子串 position从0开始 | [root@shell script]# echo $GTMS HELLO 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 $OLDBOY I 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 ===>ok rm -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 written to 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 is substituted, 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 OR expr?expr:expr conditional 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
[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 $a aaaa
[root@shell ~]# #!/bin.sh read -p "pls input a character:" a echo "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 <<EOF 1.[install lamp] 2.[install lnmp] 3.[exit] EOF
read -t 20 -p "pls..........." num [ -z "$num" ] &&{ echo "pls input right num" exit 2 }
[ $num -eq 1 ]&&{ echo " install lamp......................" #/bin/sh /server/scripts/lamp.sh exit }
[ $num -eq 2 ]&&{ echo " install lnmp......................" #/bin/sh /server/scripts/lamp.sh exit }
[ $num -eq 3 ]&&{ echo " bye" exit }
[ "$num" != 1 -o "$num" != 2 -o "$num" != 3 ]&&{ echo " input error" exit } #sh menu.sh 1.[install lamp] 2.[install lnmp] 3.[exit] pls input the num you want
定义函数menu函数
#!/bin/sh
menu() {
cat <<AA
1.[install lamp]
2.[install lnmp]
3.[exit]
AA
}
menu
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 指令3 fi
[root@shell ~]# cat comparebigsmall.sh #!/bin/bash if [ $# -ne 2 ]; then echo "USAGE:$0 arg1 arg2" exit 2 fi expr $1 + 1 &>/dev/null RETVAL1=$? expr $2 + 1 &>/dev/null RETVAL2=$? if [ $RETVAL1 -ne 0 -a $RETVAL2 -ne 0 ];then echo "please input two int" exit 3 fi if [ $RETVAL1 -ne 0 ];then echo "the first num is not int,pls input again" exit 4 fi if [ $RETVAL2 -ne 0 ];then echo "the second num is not int,pls input again" exit 5 fi if [ $1 -lt $2 ];then echo "$1<$2" exit elif [ $1 -eq $2 ];then echo "$1=$2" exit else echo "$1>$2" exit fi |
[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=$1 b=$2 if [ $# -ne 2 ]; then echo "USAGE:$0 arg1 arg2" exit 2 fi expr $a + 1 &>/dev/null RETVAL1=$? expr $b + 1 &>/dev/null RETVAL2=$? if [ $RETVAL1 -ne 0 -a $RETVAL2 -ne 0 ];then echo "please input two int" exit 3 fi if [ $RETVAL1 -ne 0 ];then echo "the first num is not int,pls input again" exit 4 fi if [ $RETVAL2 -ne 0 ];then echo "the second num is not int,pls input again" exit 5 fi if [ $a -lt $b ];then echo "$a<$b" exit elif [ $a -eq $b ];then echo "$a=$b" exit else echo "$a>$b" exit fi |
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 |
[root@shell ~]# cat functest.sh
|
实例
[root@shell ~]# cat checkweb.sh |
[root@shell ~]# cat checkweb.sh #!/bin/bash usage() { echo "USAGE:$0 arg" exit 1 } check_url() { curl -s $1 &>/dev/null if [ $? -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/functions nginx="/usr/local/nginx/sbin/nginx" prog="nginx" RETVAL=0 start() { 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}" esac exit $RETVAL cp nginx.sh /etc/init.d/nginx chmod +x nginx.sh nginx脚本中添加#!/bin/sh下 #chkconfig: 2345 54 65 chkconfig --add nginx chconfig nginx on chkconfig --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 <file sum=0 while read line do cmd done |
cat ${file_path} | while read line do cmd done |
while read line do cmd done<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<access.log
echo $sum
while循环小结
1、while循环的特长是执行守护进程以及我们希望循环不退出持续执行的情况,用于频率小于1分钟的循环处理,其他的while几乎可以被for 循环替代
2、case语句可以if语句替换,一般在系统脚本传入少量固定规则字符串用case,其他普通判断多用if
3、if for多用,其次while(守护进程),case(启动脚本)
各语句使用场景
条件表达式 简短的判断
if 取值判断
while 守护进程无限循环(sleep)
case 服务启动脚本,菜单
for 正常的循环处理,最常用
函数 减少重复语句
for循环结构
for循环结构语法 for 变量名 in 取值列表 do 指令 done c语言型for循环结构
for ((expr1;expr2;expr3))
do
指令
done seq -w 10 01 02 ~~~~~ #直接列出变量列表所有元素,打印5 4 3 2 1 for num in 5 4 3 2 1 do echo 10.0.0.$num done for 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/sh RED_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 <user.pass
随机数
RANDOM随机数 echo $RANDOM 随机数0-32767 echo "$RANDOM gtms" | md5sum 通过openssl产生随机数 openssl rand -base64 8 openssl rand -base64 10 通过时间函数获得随机数 date +%s%N 通过uuid [root@shell ~]# cat /proc/sys/kernel/random/uuid f69ee9b7-fe0c-4173-b07c-5a881f7a739a [root@shell ~]# cat /proc/sys/kernel/random/uuid cbd8b0e9-24cc-44dd-9d96-dec53d7624a4 使用mkpasswd 生成随机数 yum install expect -y [root@test88 script]# mkpasswd -l 8 yWG8u\6a [root@test88 script]# mkpasswd -l 9 vq18MapV- 对于长短不一的随机数 使用md5sum | cut -c 1-9 格式化