Shell学习笔记(四):必备知识篇

循环控制及状态返回值的应用实践

break、continue、exit、return的区别和对比

s-56

例1:break跳出整个循环,执行循环下面的其他程序

#!/bin/sh
for((i=0;i<=5;i++))
do
    if [ $i -eq 3 ];then
        #continue;
        break;
        #exit
    fi
        echo $i
done
echo "ok"

例2(生产场景):开发shell脚本实现给服务器临时配置多个别名IP,并可以随时撤销配置的所有
IP。IP地址为:10.0.2.1-10.0.2.16,其中10.0.2.10不能设置。

#!/bin/sh
[ -f /etc/init.d/functions ] && . /etc/init.d/functions
RETVAL=0
op(){
    if [ "$1" == "del" ];then
        list=`echo {16..1}`
    else
        list=`echo {1..16}`
    fi
    for ip in $list;do
        if [ $ip -eq 10 ];then
            continue
        fi
        ip addr $1 10.0.2.$ip/24 dev ens33 label ens33:$ip &>/dev/null
        RETVAL=$?
        if [ $RETVAL -eq 0 ];then
            action "$1 $ip" /bin/true    #此处的提示用通用的$1,传参来控制
        else
            action "$1 $ip" /bin/false
        fi
    done
    return $RETVAL
}
case "$1" in
    start)
        op add                          #启动时,就传参add给op函数
        RETVAL=$?
        ;;
    stop)
        op del                          #停止时,就传参del给op函数
        RETVAL=$?
        ;;
    restart)
        op del
        sleep 2
        op add
        RETVAL=$?
        ;;
    *)
        printf "USAGE:$0 {start|stop|restart}\n"
esac
exit $RETVAL

shell函数语法

语法格式

简单的语法:

函数名(){
                指令...
                return n
}

规范的语法:

function 函数名(){
                指令...
                return n
}

shell函数执行

调用函数:

1)直接执行函数名即可。注意,不需要带小括号。

函数名

2)带参数的函数执行方法:

函数名 参数1 参数2

函数带参数的说明

  • 在函数体中位置参数($1、$2、$3、$4、$5、$#、$*、$?以及$@)都可以是函数参数
  • 父脚本的参数则临时地被函数参数所掩盖或隐藏
  • $0比较特殊,他仍然是父脚本的名称
  • 当函数完成时,原来的命令行参数会恢复
  • 在shell函数里面,return命令的功能与工作方式与exit相同,用于跳出函数
  • 在shell函数体里使用exit会终止整个shell脚本
  • return语句会返回一个退出值给调用的程序。

例:编写shell开发linux系统一键优化脚本。

1、安装系统是最小化安装

2、配置国内高速yum源

3、禁用开机不需要启动的服务

4、优化系统内核参数/etc/sysctl.conf

5、增加系统文件描述符、堆栈等配置

6、禁止root远程登录。修改ssh端口为特殊端口,禁止DNS,空密码

7、有外网ip的机器要开启配置防火墙,仅对外开启需要提供服务的端口,配置或关闭selinux

8、清除无用的默认系统账户或组(非必须)(添加运维的用户)

9、锁定敏感文件,如/etc/passwd(非必须)

10、配置服务器和互联网时间同步

11、初始化用户,并配置sudo对普通用户权限的控制

12、修改系统字符集

13、补装系统软件及升级系统到最新

#!/bin/bash
export PATH=$PATH:/bin:/sbin:/usr/sbin
# Require root to run this script.
if [ "$UID" != "0" ]; then
    echo "Please run this script by root."
    exit 1
fi

#define cmd var
SERVICE=`which service`
CHKCONFIG=`which chkconfig`

function mod_yum(){
    #modify yum path
    if [ -e /etc/yum.repos.d/CentOS-Base.repo ]
     then
       mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup&&\
       wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.sise.edu.cn/repo/CentOS-Base.repo
    fi
}

function close_selinux(){
    #1.close selinux
    sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
    #grep SELINUX=disabled /etc/selinux/config
    setenforce 0 &>/dev/null
    #getenforce
}

function close_firewall(){
    #2.close firewall
    systemctl stop firewalld
    systemctl stop firewalld
    systemctl disable firewalld
}

function least_service(){
    #3.least service startup
    chkconfig|awk '{print "chkconfig",$1,"off"}'|bash
    chkconfig|egrep "crond|sshd|network|rsyslog|sysstat"|awk '{print "chkconfig",$1,"on"}'|bash
    #export LANG=en
    #chkconfig --list|grep 3:on
}

function adduser(){
    #4.add yjscloud and sudo
    if [ `grep -w yjscloud /etc/passwd|wc -l` -lt 1 ]
      then
        useradd yjscloud
        echo 123456|passwd --stdin yjscloud
        \cp /etc/sudoers /etc/sudoers.ori
        echo "yjscloud  ALL=(ALL) NOPASSWD: ALL " >>/etc/sudoers
        tail -1 /etc/sudoers
        visudo -c &>/dev/null
    fi
}

function charset(){
    #5.charset config
    cp /etc/locale.conf /etc/locale.conf
    echo 'LANG="zh_CN.UTF-8"'  >/etc/locale.conf
    source /etc/locale.conf
    #echo $LANG
}

function time_sync(){
    #6.time sync.
    cron=/var/spool/cron/root
    if [ `grep -w "ntpdate" $cron|wc -l` -lt 1  ]
      then
        echo '#time sync by yjscloud at 2018-6-24' >>$cron
        echo '*/5 * * * * /usr/sbin/ntpdate cn.pool.ntp.org >/dev/null 2>&1' >>$cron
        crontab -l
    fi
}

function com_line_set(){
    #7.command set.
    if [ `egrep "TMOUT|HISTSIZE|HISTFILESIZE" /etc/profile|wc -l` -ge 3  ]
      then
        echo 'export TMOUT=300' >>/etc/profile
        echo 'export HISTSIZE=5' >>/etc/profile
        echo 'export HISTFILESIZE=5' >>/etc/profile
        . /etc/profile
    fi
}

function open_file_set(){
    #8.increase open file.
    if [ `grep 65535 /etc/security/limits.conf|wc -l` -lt 1 ]
      then 
        echo '*               -       nofile          65535 ' >>/etc/security/limits.conf
        tail -1 /etc/security/limits.conf
    fi
}

function set_kernel(){
    #9.kernel set.
    if [ `grep kernel_flag /etc/sysctl.conf|wc -l` -lt 1 ]
      then
        cat >>/etc/sysctl.conf<<EOF
        #kernel_flag
        net.ipv4.tcp_fin_timeout = 2
        net.ipv4.tcp_tw_reuse = 1
        net.ipv4.tcp_tw_recycle = 1
        net.ipv4.tcp_syncookies = 1
        net.ipv4.tcp_keepalive_time = 600
        net.ipv4.ip_local_port_range = 4000    65000
        net.ipv4.tcp_max_syn_backlog = 16384
        net.ipv4.tcp_max_tw_buckets = 36000
        net.ipv4.route.gc_timeout = 100
        net.ipv4.tcp_syn_retries = 1
        net.ipv4.tcp_synack_retries = 1
        net.core.somaxconn = 16384
        net.core.netdev_max_backlog = 16384
        net.ipv4.tcp_max_orphans = 16384
EOF
        sysctl -p
    fi
}
function init_ssh(){
    \cp /etc/ssh/sshd_config /etc/ssh/sshd_config.`date +"%Y-%m-%d_%H-%M-%S"`
    #sed -i 's%#Port 22%Port 52113%' /etc/ssh/sshd_config
    sed -i 's%#PermitRootLogin yes%PermitRootLogin no%' /etc/ssh/sshd_config
    sed -i 's%#PermitEmptyPasswords no%PermitEmptyPasswords no%' /etc/ssh/sshd_config
    sed -i 's%#UseDNS yes%UseDNS no%' /etc/ssh/sshd_config
    /etc/init.d/sshd reload &>/dev/null
}

function update_linux(){
    #10.upgrade linux.
    if [ `rpm -qa lrzsz nmap tree dos2unix nc|wc -l` -le 3 ]
      then
        yum install lrzsz nmap tree dos2unix nc ntpdate -y
        #yum update -y
    fi
}
main(){
    mod_yum
    close_selinux
    close_firewall
    least_service
    adduser
    charset
    time_sync
    com_line_set
    open_file_set
    set_kernel
    init_ssh
    update_linux
}
main

shell数组

简单的说,数组就是相同数据类型的元素按一定顺序排列的集合。数组就是把有限个类型相同的变量用一个名字命名,然后用编号区分他们的变量的集合。这个名字成为数组名,编号成为下标。组成数组的各个变量成为数组的分量,也称为数组的元素,有时也称为下标变量。如果有过用其它语言编程的经历,那么想必会熟悉数组的概念。由于有了数组,可以用相同名字引用一系列变量,并用数字(索引)来识别它们。在许多场合,使用数组可以缩短和简化程序,因为可以利用索引值设计一个循环,高校处理多种情况。

数组定义与读取

方法1:array=(value1 value2 value3)

1)定义数组

[root@pan ~]# array=(1 2 3)   #对括号表示数组,数组元素用“空格”符号分割开

2)获取数组的长度

[root@pan ~]# echo ${#array[@]}    #用${#数组名[@或*]}可以获得到数组长度
3
[root@pan ~]# echo ${#array[*]}
3

3)打印数组元素

[root@pan ~]# echo ${array[0]}   #打印数组元素用${数组名[下标]}下标是从0开始
1
[root@pan ~]# echo ${array[1]}
2
[root@pan ~]# echo ${array[2]}
3
[root@pan ~]# echo ${array[3]}  #超出范围输出为空

[root@pan ~]# echo ${array[*]}   #下标是:*或者@得到整个数组内容
1 2 3
[root@pan ~]# echo ${array[@]}  #把前面去数组长度的#号去掉,例如:${array[@]}
1 2 3

4)数组赋值

直接通过数组名[下标]就可以对其进行引用赋值,如果下标不存在,自动添加新一个数组元素,如果存在就覆盖原来的值

[root@pan ~]# array[3]=4
[root@pan ~]# echo ${array[@]}
1 2 3 4
[root@pan ~]# array[0]=pan
[root@pan ~]# echo ${array[@]}
pan 2 3 4

5)数组删除

直接通过:unset数组[下标]可以清除相应的元素,不带下标,清除整个数据

[root@pan ~]# echo ${array[@]}
pan 2 3 4
[root@pan ~]# unset array    #删除整个数组
[root@pan ~]# echo ${array[@]}

[root@pan ~]# array=(1 2 3)
[root@pan ~]# unset array[0]    #删除某个数组元素
[root@pan ~]# echo ${array[@]}
2 3

6)数组内容的截取和替换(和前文变量子串的替换很像)

截取:

[root@pan ~]# array=(1 2 3 4 5)
[root@pan ~]# echo ${array[@]:1:3}     #截取1号到3号数组元素
2 3 4
[root@pan ~]# echo ${array[@]:3:2}
4 5

替换:

[root@pan ~]# echo ${array[@]/5/6}    #把数组中的5替换成6,临时生效,原数组未被修改。和sed很像。
1 2 3 4 6
[root@pan ~]# array1=(${array[@]/5/6})
[root@pan ~]# echo ${array[@]}
1 2 3 4 5

调用方法是:${数组名[@或*]/查找字符/替换字符}该操作不会改变原先数组内容,如果需要修改,可以看上面例子,重新定义数组

删除:

[root@pan ~]# array1=(one two three four five)
[root@pan ~]# echo ${array1[@]}
one two three four five
[root@pan ~]# echo ${array1[@]#o}    #左边开始最短的匹配
ne two three four five
[root@pan ~]# echo ${array1[@]#fo}    #左边开始最短的匹配
one two three ur five
[root@pan ~]# echo ${array1[@]%t*e}
one two four five
[root@pan ~]# echo ${array1[@]%%t*e}
one two four five

提示:数组也是变量,因此也适合于前面讲解过的变量的子串处理的功能应用。

方法2:array=([1]=one [2]=two [3]=three)

[root@pan ~]# array=([1]=one [2]=two [3]=three)
[root@pan ~]# echo ${#array[*]}
3
[root@pan ~]# echo ${array[*]}
one two three

方法3:array[0]=a array[1]=b array[2]=c

[root@pan ~]# array[0]=a
[root@pan ~]# array[1]=b
[root@pan ~]# array[2]=c
[root@pan ~]# array[3]=d
[root@pan ~]# echo ${array[0]}
a
[root@pan ~]# echo ${array[2]}
c

数组实践

例1:通过列举法打印数组元素

array=(
xwq
xdf
yjs
)

for ((i=0; i<${#array[*]};i++));do
    echo "This is num $i,then content is ${array[$i]}"
done
echo ------------
echo "array len:${#array[*]}"

执行效果:

[root@pan shell]# sh array01.sh
This is num 0,then content is xwq
This is num 1,then content is xdf
This is num 2,then content is yjs
------------
array len:3

例2:把系统命令结果做为数组元素

#!/bin/sh
dir=($(ls))
for ((i=0;i<`echo ${#dir[@]}`;i++));do
    echo ${dir[$i]}
done

例3:批量检查多个网站地址是否正常

要求:

1)使用shell数组的方法实现,检测策略尽量模拟用户访问

2)每10秒进行一次全部检测,无法访问的输出做出报警

#!/bin/bash
. /etc/init.d/functions
check_count=0
url_list=(
blog.yjscloud.com
mirrors.sise.edu.cn
yjsxwq.com
192.168.0.15
)

function wait()
{
    echo -n '3秒后,执行检查URL操作.';
    for ((i=0;i<3;i++)); do
        echo -n ".";sleep 1
    done
    echo
}
function check_url()
{
    wait
    for ((i=0; i<`echo ${#url_list[*]}`; i++));do
        wget -o /dev/null -T 3 --tries=1 --spider ${url_list[$i]} >/dev/null 2>&1
        if [ $? -eq 0 ];then
            action "${url_list[$i]}" /bin/true
        else
            action "${url_list[$i]}" /bin/false
        fi
done
((check_count++))
}
main(){
    while true
    do
    check_url
    echo "-------check count:${check_count}---------"
    sleep 7
    done
}
main

脚本语法检查

对脚本的调试如果把windows下编辑的脚本放置到linux下执行,最好执行dos2unix格式化一下,执行dos2unix格式化是一个很好的习惯。如果没有安装dos2unix,则使用yum -y install dos2unix进行安装。使用echo调试,echo命令是最有用的调试脚本的工具之一。一般应在可能出现问题的脚本的重要部分加入echo命令。例如在变量读取或修改操作的前后加入echo命令,并紧挨着退出命令exit。

使用bash命令参数调试

参数说明:

-n:不会执行该脚本,仅查询脚本语法是否有问题,并给出错误提示。

-v:在执行脚本时,先将脚本的内容输出到屏幕上,然后执行脚本,如果有错误,也会给出错误提示。

-x:将执行的脚本内容及输出显示到屏幕上,这对调试很有用的参数。

使用set命令调试部分脚本内容

set命令也可以用于辅助脚本调试,下面是set命令常用的调用选项

set -n:读命令但并不执行

set -v:显示读取的所有行

set -x:显示所有命令及其参数

提示:通过set -x命令开启调试功能,而通过set +x关闭调试功能,和bash -x相比set -x可以缩小调试的作用域。

例:打印九九乘法表的简版的脚本

#!/bin/sh
set -x
for a in `seq 9`
do
    for b in `seq 9`
    do
        [ $a -ge $b ] && echo -en "$a x $b = $(expr $a \* $b) "
    done
set +x
echo " "
done

小结:

1)直接执行脚本根据报错来调试

2)要记得首先用dos2unix处理脚本

3)sh -x/-n调试整个脚本

4)set -xset +x调试部分脚本的执行过程(脚本中设置)

5)可通过echo命令输出脚本中要确认的变量及相关内容;然后紧跟着使用exit退出,不执行后面程
序,这种方式便于一步步跟踪脚本。对于逻辑错误的调试比较好用写法即echo $var;exit

6)最关键的还是要语法熟练,养成良好的编码习惯。

|| 版权声明
作者:废权
链接:https://blog.yjscloud.com/archives/78
声明:如无特别声明本文即为原创文章仅代表个人观点,版权归《废权的博客》所有,欢迎转载,转载请保留原文链接。
THE END
分享
二维码
Shell学习笔记(四):必备知识篇
循环控制及状态返回值的应用实践 break、continue、exit、return的区别和对比 例1:break跳出整个循环,执行循环下面的其他程序 #!/bin/sh for((i=0;i<=5;……
<<上一篇
下一篇>>
文章目录
关闭
目 录