shell编程

什么是shell

Shell是一个命令解释器,就像是包裹在Linux内核外面的一层外壳,在Linux的终端中我们输入命令后计算机的内核并不能直接执行这些命令,
我们都知道计算机执行的是二进制的代码,而Shell的作用就正好是将这些命令解释成计算机可以执行的二进制代码,而内核就可以执行这些二进制代码,
内核可以控制计算机的硬件工作比如:声卡、网卡等计算机硬件。优势在于处理操作系统底层的业务,有大量的linux系统命令为它作支撑,特别是grep awk sed等。

学好 shell编程并实现通过shell脚本自动化管理系统必备基础   
1vi/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

 

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 
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/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语法
 casein
  模式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
nginx起停脚本参考
 

 

 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 格式化

 

posted @ 2017-05-01 02:19  黑色月牙  阅读(351)  评论(0编辑  收藏  举报