shell高级部分总结

给脚本加锁

通过创建文件,来给脚本加锁,脚本执行完成只有一定记得要删除文件!

写一个rsync的启动和停止脚本flock这个命令,-x 排它锁n :指定锁文件位置flock -xn "/tmp/f.lock" -c 'a.sh'  运行结束后自动退出这就就能避免多实例重复运行​​在脚本里面,可以通过创建文件和删除文件来保证只有个一个脚本实例运行​[ ! -f /tmp/flock.pid ] && {touch /tmp.flock.pid  #创建一个锁文件!   有的服务天生就有pid这个文件位置,不比nginx!这时候就不用创建了​执行你的代码​rm -rf /tmp/flock.pid #最后一定要将锁文件删除!} || {    echo "当前剧本存在运行的实例...请稍后..."}​​​根据系统版本,yum不同的软件tt-$(awk '{print $(NF-1)}' /etc/redhet-release)echo ${tt%%.*} #*号通配符,一直配到点.为止​​

捕获信号

当我们按ctrl+c或者ctrl+z的时候,脚本就会被终止,为了防止这种情况发生,就需要将这些按键的信号捕捉后处理下,防止正在运行的脚本中断;

ctrl-c 发送SIGINT 信号给前台进程组中的所有进程。常用于终止正在运行的程序。ctrl-z 发送SIGTSTP信号给前台进程组中的所有进程,常用于挂起一个进程。ctrl-d不是发送信号,而是表示一个特殊的二进制值,表示 EOF。ctrl-/发送SIGQUIT信号给前台进程组中的所有进程,终止前台进程并生成core 文件。kill -9的信号是无法捕捉的!!​trap "" HUP INT TSTP​

函数

方式一:function han {}方式二:han2(){}两种方式任选一种即可;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;​判断某个进程是否活着;[root@lianxi ~]# vim 12.shfunction checkpid {    local i  #防止外面变量污染i,也防止污染外部变量i;只在当前函数内有效;    for i in $*;do        [ -d "/proc/$i" ] && return 0 #相当于if 语句了;    done    return 1 #如果进程不存在,那么就返回1}​[root@lianxi ~]# source 12.sh  将函数加载到shell变量中,这个一定不能省!!!        #运行的时候,一定要用soruce或者. 的方式,防止在子shell中无法产生效果​[root@lianxi ~]# checkpid 1  #检查1号进程是否存在;这样定义在文件中的函数,在命令行就可以直接执行了​函数执行完成后,默认整个函数的状态码为函数内部最后一个命令的返回值,之前提到的exit命令可以自定义返回码,但是在函数中如果使用exit命令就会导致整个脚本直接退出。在函数中我们可以使用return命令立刻让函数终端并返回特定的状态码,并且不会影响脚本中后续命令的执行.

return只能返回特定的状态码(数字),不能是变量或其他字符。

函数参数和位置参数没关系

脚本的位置参数和函数的位置参数没有关系!在函数内部也可以使用$1,$0...$n 的方式向函数传递参数

[root@web01 ~]# func1(){> echo "hello $1"> }[root@web01 ~]# func1hello [root@web01 ~]# func1 ceshihello ceshi​#直接就给函数传递参数了,但是同样的可一个脚本传递参数[root@web01 ~]#vim 1.shfunc1 $1  #在脚本里给函数传参!​[root@web01 ~]# bash 1.sh ceshi  #一样的​2. 使用变量方式传递固定值[root@web01 ~]# cat 1.sh #!/bin/bashfunction han {    echo "$num" }num=10 # 传递参数han ​​​​#看看下面这个例子[root@web01 ~]# cat 1.sh #!/bin/bashfunction han {    echo "函数参数为$1"  #每次给函数的都是一个参数,第一个参数就是$1;第二个参数是$2}​han $1   #相当于han test1 han $2   #相当于han test2 接受脚本的第二个位置参数,传入函数中算第一个参数!han $3   #相当于han test3​所以函数的参数和脚本参数互不影响;[root@web01 ~]# bash 1.sh test1 test2 test3函数参数为test1函数参数为test2函数参数为test3​​#这一步就能看出脚本参数和函数参数没有对应关系[root@web01 ~]# cat 1.sh #!/bin/bash​function han {    echo "函数参数为$1,$2,$3"​}han $2 $1 $3 #将脚本的位置参数乱序 传入函数中[root@web01 ~]# bash 1.sh 10 20 30函数参数为20,10,30​​​​

函数状态返回

Shell的函数返回值,也算是退出的状态。在shell中只有echo、return两种方式。

  1. 使用return返回值:只能返回1-255的整数,函数使用return返回值,通常只是用来供其他地方调用获取状态,因此通常仅返回0或1; 0表示成功,1表示失败。

  2. 使用echo返回值:使用echo可以返回任何字符串结果 通常用于返回数据,比如一个字符串值或者列表值

一个用来返回值,一个返回的是状态码(一定要放在最后)[root@web01 ~]# cat 1.sh #!/bin/bash​function han {    echo "函数返回值为1" #返回函数执行后的数据    return 12   #返回函数执行的状态码(放置最后!)            #所以在函数中的echo是用来返回值的,并不会在终端上打印!!!!!!!!!!!}ret=`han`   ret用来接受函数的返回值!echo "函数的状态码是:$?"echo "函数的返回值是:$ret"[root@web01 ~]# bash 1.sh 函数的状态码是:12函数的返回值是:函数返回值为1​​通过$?号进行判断的!if [ $? -eq 12 ];then    echo "返回值是12"else    echo "其他数值"fi​return只能用在函数里面,不属于脚本的返回值;​​为啥有函数返回值?如果一个脚本中有很多函数,可以给不同的函数返回不同的值来确定是哪个函数出错了!

脚本的返回值

要区分脚本的返回值和函数的返回值;脚本的只能用exit;

在终端可以通过$?判断一个命令是否真确执行;在shell脚本中可提供了方法,那就是exit;只有这一种!!!​​​exit默认返回上一条命令的致执行结果,并且退出当前shell[root@lianxi ~]# vim 1.shpwddddexit​[root@lianxi ~]# bash 1.sh  #注意是生成子shell执行,exit退出的也是子shell;如果source会退出登录1.sh: line 3: pwdddd: command not found[root@lianxi ~]# echo $?127​​自定义返回值[root@lianxi ~]# vim 1.shpwddddexit 123 #不管上面是否执行正确都返回123[root@lianxi ~]# echo $?123​程序错误不仅有语法错误, 还有逻辑错误。程序出现语法错误,你通过echo $? 可以判断是否正确执行;那逻辑错误,如果也想利用这种方法就需要自定义exit的返回值,所以exit非0 ,往往写在业务逻辑判断语句下面,如果业务逻辑出错,返回一个和小伙伴协商好的错误编号, 如果业务逻辑正确就返回0,这是它的用法

系统脚本

系统自建了一个函数库,可以在脚本中使用,/etc/init.d/functions定义了服务常用的函数,如果想使用这些函数就用source导入函数,类似于Python的import

[root@lianxi ~]# cat /etc/init.d/functions  #系统函数库系统提供了很多函数,比如下面输出echo_success() {    [ "$BOOTUP" = "color" ] && $MOVE_TO_COL    echo -n "["    [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS    echo -n $"  OK  "    [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL    echo -n "]"    echo -ne "\r"    return 0}[root@lianxi ~]# source /etc/init.d/functions[root@lianxi ~]# echo_success [root@lianxi ~]#                                           [  确定  ]  #正确启动就是确定,失败就是红色的失败​可以将这个复制到你自定义的服务启动脚本中,然后漂亮的输出!!

数组

数组本质也是变量,传统的变量只能存储一个值,但数组可以存储多个值;

数组的分来:shell数组分为普通数组和关联数组;

[root@web01 ~]# a1=123      #普通变量只能存一个值[root@web01 ~]# echo $a1   123[root@web01 ~]# a2=(1 2 3 4)   #数组可以存多个值[root@web01 ~]# echo ${a2[3]}  #下标从0开始4[root@web01 ~]# echo ${a2[@]}  #${a2[*]} 输出所有数组1 2 3 4

普通数组:

只能使用整数作为数组索引;类似于Python中的列表

[root@web01 ~]# a2=(1 2 3 4) 这就是普通数组,普通数组下标只能是整数,从0开始``或者$()执行的结果以数组方式存储起来的![root@web01 ~]# a2=(`cat /etc/passwd`)  cat一行一行的读取出数据,然后赋值到数组当中;[root@web01 ~]# declare -a  #查看数组declare -a a2='([0]="root:x:0:0:root:/root:/bin/bash" [1]="bin:x:1:1:bin:/bin:/sbin/nologin" [2]="daemon:x:2:2:daemon:/sbin:/sbin/nologin" [3]="adm:x:3:4:adm:/var/adm:/sbin/nologin" [4]="lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin" [5]="sync:x:5:0:sync:/sbin:/bin/sync" [6]="shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown" [7]="halt:x:7:0:halt:/sbin:/sbin/halt" [8]="mail:x:8:12:mail:/var/spool/mail:/sbin/nologin" [9]="operator:x:11:0:operator:/root:/sbin/nologin" [10]="games:x:12:100:games:/usr/games:/sbin/nologin" [11]="ftp:x:14:50:FTP" [12]="User:/var/ftp:/sbin/nologin" [13]="nobody:x:99:99:Nobody:/:/sbin/nologin" [14]="systemd-network:x:192:192:systemd" [15]="Network" [16]="Management:/:/sbin/nologin" [17]="dbus:x:81:81:System" [18]="message" [19]="bus:/:/sbin/nologin" [20]="polkitd:x:999:998:User" [21]="for" [22]="polkitd:/:/sbin/nologin" [23]="tss:x:59:59:Account" [24]="used" [25]="by" [26]="the" [27]="trousers" [28]="package" [29]="to" [30]="sandbox" [31]="the" [32]="tcsd" [33]="daemon:/dev/null:/sbin/nologin" [34]="sshd:x:74:74:Privilege-separated" [35]="SSH:/var/empty/sshd:/sbin/nologin" [36]="postfix:x:89:89::/var/spool/postfix:/sbin/nologin" [37]="test1:x:1000:1000::/home/test1:/bin/bash" [38]="oldboy05:x:1005:1005::/home/oldboy05:/bin/bash" [39]="xuyang:x:1011:1011::/home/xuyang:/bin/bash" [40]="Alex:x:1012:1012::/home/Alex:/sbin/nologin" [41]="alex02:x:1013:1013::/home/alex02:/bin/bash" [42]="ntp:x:38:38::/etc/ntp:/sbin/nologin" [43]="tcpdump:x:72:72::/:/sbin/nologin" [44]="nginx:x:998:996:Nginx" [45]="web" [46]="server:/var/lib/nginx:/sbin/nologin")'​​​​​

关联数组

可以使用字符串作为数组索引;类似于Python中的字典

[root@web01 ~]# declare -A info  #定义关联数组一定要先声明一下!在赋值这样才可以![root@web01 ~]# info=([name]=xuyang [age]=18 [skill]=linux) 类似于Python中的字典,key:value形式[root@web01 ~]# echo ${info[name]}xuyang[root@web01 ~]# echo ${info[@]}  #所有的value;xuyang 18 linux[root@web01 ~]# echo ${!info[@]} #所有的key;name age skill

数组的遍历

推荐通过数组元素的索引进行遍历;不推荐通过元素的个数进行遍历;

将统计的对象作为数组的索引仅针对关联数据;

后面有awk更快捷的方式,但是这个也要学

普通数组赋值与遍历是列

1.使用关联数组统计文件中男女性别个数jack malice ftom mrose frobin mbgx m​[root@web01 ~]# awk '{print $2}' 2.txt | sort | uniq -c  #一条命令搞定,      2 f      4 m数组的解决方法​太难了去你妈的,不写了P727开始

shell交互与非交互

交互式:是我们最常用的模式、登录终端、执行命令等待结果,退出shell会话就会终止;(会执行5个环境变量文件)

非交互式:shell直接读取某个文件进行执行;文件执行完后,shell也会终止;(不执行任何环境变量文件)

脚本资源控制

  • 脚本优先级控制 就是占用资源的分配,通过nice和renice调整脚本CPU占用优先级;避免出现不可控的死循环

    死循环导致CPU占用过高 死循环导致死机

    避免产生大量子进程(成为fork炸弹),也会将CPU大量消耗

    [root@lianxi ~]# ulimit -a #查看终端限制,但是对root不起作用core file size          (blocks, -c) 0data seg size           (kbytes, -d) unlimitedscheduling priority             (-e) 0file size               (blocks, -f) unlimitedpending signals                 (-i) 7184max locked memory       (kbytes, -l) 64max memory size         (kbytes, -m) unlimitedopen files                      (-n) 1024pipe size            (512 bytes, -p) 8POSIX message queues     (bytes, -q) 819200real-time priority              (-r) 0stack size              (kbytes, -s) 8192cpu time               (seconds, -t) unlimitedmax user processes              (-u) 7184virtual memory          (kbytes, -v) unlimitedfile locks                      (-x) unlimited​​fork炸弹,大量占用CPU,导致系统不响应其他服务请求[root@lianxi ~]# : | : &  #如果不加&,只有等待前面执行完了才会继续执行,所以实现不了大量创建子进程: | : #这就是创建两个进程,这是前台的,如果想在创建进程就要等待这个创建结束,所以要加上 & ,后台执行; ​​一旦形成fork炸弹,cntrl+C 是不启作用的,但是普通用户收到了max user processes限制,fork炸弹形成不了,如果是root,服务器就完了;​[root@lianxi ~]# cat multi_ping.sh #/bin/bashnet="192.168.2."multi_ping(){    ping -c 2 -i 0.2 -W 1 $1 >/dev/null    if [ $? -eq 0 ];then    echo "$1 is up"    else    echo "$1 is down"    fi}​​for i in {1..100}do    multi_ping $net$i &    #调用函数并放入后台执行done​Wait #所有后台进程都结束在退出写脚本

    执行该脚本时立刻新打开一个终端,使用ps命令查看进程列表,会发现同时启动了几百个进程,对于ping这样的小程序还好,如果是一个非常消耗CPU、内存、磁盘I/O资源的程序,启动几百个这样的程序,系统将瞬间崩溃。ps aux | grpe ping |wc -l

    那么我们如何限制一次启动进程的数量呢?比如一次仅启动10个进程,等待10个进程都结束再启动10个,以此类推。这里就需要引入另外两个概念:文件描述符和命名管道。

除了while 和for会创建死循环,函数也会大量创建子进程;所以在生成环境下一定要注意是否会造成不可预期,不可控的死循环,有了的话可以使用renice,nice降低级别;

  • 信号的控制

    捕获信号脚本的编写,kill默认会发送15号信号给应用程序,ctrl+c发送2号信号,9号信号不可阻塞也不能被捕获;

    捕获信号的作用:例如脚本对关键数据备份,不希望被15/2等信号所中断,造成备份不完全,所以将信号捕获主,然后处理;

    #/bin/bashtrap "echo sig 15 " 15 #捕获15进程号,双引号中的抓取到的信号后执行的命令trap "echo sig 2 " 2echo $$    #打印当前进程号​while :   #死循环,就是为了夯住这个脚本do:done[root@lianxi ~]# bash signal.sh 2535sig 15    发送15信号[root@lianxi ~]# kill -15 2535,程序不会结束​​​kill -9   是不可以被捕获和阻塞的,所以写在脚本中也没用

     

请登录后发表评论

    没有回复内容