首页 > 系统服务 > 详细

shell脚本进阶

时间:2020-05-10 09:43:21      阅读:57      评论:0      收藏:0      [点我收藏+]

一级标题

shell脚本进阶

二级标题

1循环
1.1循环执行介绍
技术分享图片

将某代码段重复运行多次,通常有进入循环的条件和退出循环的条件
重复运行次数
循环次数事先已知
循环次数事先未知
常见的循环的命令:for, while, until

技术分享图片

1.2 for循环
[root@centos7 ~]#help for
for: for NAME [in WORDS ... ] ; do COMMANDS; done
Execute commands for each member in a list.

The for‘ loop executes a sequence of commands for each member in a ?list of items. If in WORDS ...;‘ is not present, then `in "$@"‘ is
?assumed. For each element in WORDS, NAME is set to that element, and
?the COMMANDS are executed.
?
?Exit Status:
?Returns the status of the last command executed.
for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done
?Arithmetic for loop.
?
?Equivalent to
?(( EXP1 ))
?while (( EXP2 )); do
?COMMANDS
?(( EXP3 ))
?done
?EXP1, EXP2, and EXP3 are arithmetic expressions. If any expression is
?omitted, it behaves as if it evaluates to 1.
?
?Exit Status:
?Returns the status of the last command executed.
[root@centos7 ~]#

格式1:
for NAME [in WORDS ... ] ; do COMMANDS; done

for 变量名 in 列表;do
    循环体
done

for 变量名 in 列表
do
    循环体
done

执行机制:
依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束

for循环列表生成方式:
直接给出列表
整数列表:
{start..end}
$(seq [start [step]] end)

返回列表的命令:
$(COMMAND)
使用glob,如:*.sh
变量引用,如:$@,$*,$#

范例:面试题,计算1+2+3+...+100的结果

[root@centos8 ~]#sum=0;for i in {1..100};do let sum+=i;done ;echo sum=$sum
sum=5050

[root@centos8 ~]#seq -s+ 100|bc5050
5050

范例:
[root@centos8 ~]#cat /data/scripts/for_sum.sh
#!/bin/bash

sum=0
for i in $* ; do
?   let sum+=i
done
echo sum=$sum
[root@centos8 ~]#bash /data/scripts/for_sum.sh 1 2 3 4 5 6
sum=21

范例: 九九乘法表
#!/bin/bash
for i in {1..9};do
    for j in `seq $i`;do
        echo -e "${j}x${i}=$[i*j]\t\c"
    done
    echo
done

生产案例:将指定目录下的文件所有文件的后缀改名为 bak 后缀
[root@centos8 ~]#cat /data/scripts/for_rename.sh
#!/bin/bash
DIR=/data/test
cd $DIR
for FILE in * ;do
?  ?PRE=`echo $FILE | sed -nr ‘s/(.*)\.([^.]+)$/\1/p‘`
# ? SUFFIX=`echo $FILE | sed -nr ‘s/(.*)\.([^.]+)$/\2/p‘`
??  mv $FILE $PRE.bak
done

范例:M37期面试题,要求将目录YYYY-MM-DD/中所有文件,移动到YYYY-MM/DD/下

#1 yyyy-mm-dd10.sh 创建YYYY-MM-DD,当前日期一年前365天到目前共365个目录,里面有10个文件,
$RANDOM.log

[root@centos8 ~]#cat for_dir.sh
#!/bin/bash
for i in {1..365};do
    DIR=`date -d "-$i day" +%F`
    mkdir /data/test/$DIR
    cd /data/test/$DIR
    for n in {1..10};do
        touch $RANDOM.log
    done
done


#2 移动到YYYY-MM/DD/下 

#!/bin/bash
#
DIR=/data/test
cd $DIR
for DIR in * ;do
    YYYY_MM=`echo $DIR |cut -d"-" -f1,2`
    DD=`echo $DIR |cut -d"-" -f3`
    [ -d $YYYY_MM/$DD ] || mkdir -p $YYYY_MM/$DD &> /dev/null
    mv $DIR/* ?$YYYY_MM/$DD
done

面试题:扫描一个网段:10.0.0.0/24,判断此网段中主机在线状态,将在线的主机的IP打印出来

[root@centos8 ~]#cat /data/scripts/for_scan_host.sh
#!/bin/bash
NET=10.0.0
for ID in {1..254};do
    {
    ping -c1 -W1 $NET.$ID &> /dev/null && echo $NET.$ID is up || echo $NET.$ID
    is down
    }&
done
wait

格式2
双小括号方法,即((…))格式,也可以用于算术运算,双小括号方法也可以使bash Shell实现C语言风格
的变量操作
I=10;((I++))

技术分享图片

for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done

for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))
do
    循环体
done

说明:
控制变量初始化:仅在运行到循环代码段时执行一次
控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断

范例:
#!/bin/bash
for((sum=0,i=1;i<=100;sum+=i,i++));do ??????????

true
done
echo $sum

范例:九九乘法表
#!/bin/bash

#语法1实现
for i in {1..9};do
?   for j in `seq $i`;do
???     echo -e "${j}x$i=$((j*i))\t\c"
?   done
?echo
done

#语法2实现
for((i=1;i<10;i++));do
?   for((j=1;j<=i;j++));do
???     echo -e "${j}x$i=$((j*i))\t\c"
?   done
?   echo
done

范例:等腰三角形
[root@centos8 scripts]#cat for_triangle.sh
#!/bin/bash
read -p "请输入三角形的行数: " line
for((i=1;i<=line;i++));do
??  for((k=0;k<=line-i;k++));do
????    echo -e ‘ \c‘
?  ?done
??  for((j=1;j<=2*i-1;j++));do
????    echo -e ‘*\c‘
??  done
??  echo
done

[root@centos8 scripts]#bash for_triangle.sh
请输入三角形的行数: 10
???     ?*
???    ?***
???    *****
??    *******
??   *********
??  ***********
?  *************
? ***************
 *****************
*******************
[root@centos8 scripts]#

范例:
[root@centos8 ~]#for((;;));do echo for;sleep 1;done
for
for
for
for
for
for
for

1.3 while循环
格式:
while COMMANDS; do COMMANDS; done
while CONDITION; do
    循环体
done

说明:
CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为
“true”,则执行一次循环;直到条件测试状态为“false”终止循环,因此:CONDTION一般应该有循环控
制变量;而此变量的值会在循环体不断地被修正
进入条件:CONDITION为true
退出条件:CONDITION为false

while true; do
    循环体
done

范例:
#配置发邮件的邮箱
[root@centos8 ~]#cat .mailrc
set from=29308620@qq.com
set smtp=smtp.qq.com
set smtp-auth-user=29308620@qq.com
set smtp-auth-password=esvnhbnqocirbicf
set smtp-auth=login
set ssl-verify=ignore

[root@centos8 ~]#cat while_diskcheck.sh
#!/bin/bash
WARNING=80
while :;do
  ?USE=`df | sed -rn ‘/^\/dev\/sd/s#.* ([0-9]+)%.*#\1#p‘ |sort -nr|  head -n1`
?  if [ $USE -gt $WARNING ];then
???echo Disk will be full from `hostname -I` | mail ?-s "disk warning"
29308620@qq.com
?  fi
?  sleep 10
done

1.4 until 循环
格式:
until COMMANDS; do COMMANDS; done
until CONDITION; do
    循环体
done

说明:
进入条件: CONDITION 为false
退出条件: CONDITION 为true
无限循环
until false; do
    循环体
Done
1.4 循环控制语句 continue
continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层
格式:
while CONDITION1; do
    CMD1
    ...
    if CONDITION2; then
        continue
    fi
    CMDn
    ...
done

范例:
[root@centos8 script40]#cat continue_for.sh
#!/bin/bash
for ((i=0;i<10;i++));do
    for((j=0;j<10;j++));do
?       [ $j -eq 5 ] && continue 2
        echo $j
    done
    echo ---------------------------
done
[root@centos8 script40]#bash continue_for.sh
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4

1.5 循环控制语句 break
break [N]:提前结束第N层整个循环,最内层为第1层
格式:
while CONDITION1; do
    CMD1
    ...
    if CONDITION2; then
    break
    fi
    CMDn
    ...
done

范例:
[root@centos8 script40]#cat break_for.sh
#!/bin/bash
for ((i=0;i<10;i++));do
    for((j=0;j<10;j++));do
       [ $j -eq 5 ] && break
?      echo $j
    done
    echo ---------------------------
done

范例:
[root@centos8 script40]#cat break_for.sh
#!/bin/bash
for ((i=0;i<10;i++));do
    for((j=0;j<10;j++));do
?       [ $j -eq 5 ] && break 2
?       echo $j
    done
    echo ---------------------------
done
[root@centos8 script40]#bash break_for.sh
0
1
2
3
4

范例:
[root@centos8 script40]#cat menu.sh
#!/bin/bash
sum=0
COLOR=‘echo -e \033[1;31m‘
COLOR2=‘echo -e \033[1;32m‘
END="\033[0m"
while true;do
    echo -e "\033[33;1m\c"
    cat <<-EOF
1) 鲍鱼
2) 满汉全席
3) 龙虾
4) 燕窝
5) 帝王蟹
6) 退出
    EOF
    echo -e "\033[0m"
    read -p "请点菜(1-6): " MENU
    case $MENU in
    1|4)
        ?$COLOR‘菜价: $10‘$END
        ?let sum+=10
        ?;;
    3|5)
        ?$COLOR‘菜价: $20‘$END
        ?let sum+=20
        ?;;
    2)
        ?$COLOR‘菜价: $1000‘$END
        ?let sum+=1000
        ?;;
    6)
        ?$COLOR2"你点的菜总价格是 $sum"$END
        ?break
        ?;;
    *)
        ?echo "点错了,没有这道菜"
        ?;;
    esac
    $COLOR2"你点的菜总价格是 $sum"$END
done

  范例:
[root@centos8 script40]#cat guess.sh
#!/bin/bash
NUM=$[RANDOM%10]
while read -p "输入 0-9 之间的数字: " INPUT ;do
    if [ $INPUT -eq $NUM ];then
        echo "恭喜你猜对了!"
        break
    elif [ $INPUT -gt $NUM ];then
????        echo "数字太大了,重新猜!"
    else
????        echo "数字太小了,重新猜!"
??  fi
done

1.6 循环控制 shift 命令
shift [n] 用于将参量列表 list 左移指定次数,缺省为左移一次。
参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。while 循环遍历位置参量列表时,常用
到 shift
范例:doit.sh
#!/bin/bash
# Name: doit.sh
# Purpose: shift through command line arguments
# Usage: doit.sh [args]
while [ $You can‘t use ‘macro parameter character #‘ in math mode# -gt 0 ] # or
(( $# > 0 ))
do
    echo $*
    shift
done
./doit.sh a b c d e f g h

范例:示例:shift.sh
#!/bin/bash
#step through all the positional parameters
until [ -z "$1" ]
do
    echo "$1"
    shift
done
echo 
./shfit.sh a b c d e f g h

范例:
[root@centos8 script40]#vim shift_batch_user.sh
#!/bin/bash
if [ $# -eq 0 ];then
??  echo "Usage: `basename $0` user1 user2 ..."
??  exit
fi
while [ "$1" ];do
    if id $1 &> /dev/null;then
????    echo $1 is exist
????else
?????   useradd $1
????   ?echo "$1 is created"
????fi
??? shift
done
echo "All user is created"
[root@centos8 script40]#bash shift_batch_user.sh
Usage: shift_batch_user.sh user1 user2 ...
[root@centos8 script40]#bash shift_batch_user.sh tom alice jack
tom is exist
alice is exist
jack is created
All user is created?????????????????????????????????

1.7 while read 特殊用法
while 循环的特殊用法,遍历文件或文本的每一行
格式:
while read line; do
    循环体
done < /PATH/FROM/SOMEFILE

说明:依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line
范例:
[root@centos8 ~]#echo magedu | read X ; echo $X
[root@centos8 ~]#echo magedu | while read X ; do echo $X;done
magedu
[root@centos8 ~]#echo magedu | { read X ; echo $X; }
magedu
[root@centos8 ~]#echo magedu | ( read X ; echo $X )
magedu
[root@centos8 ~]#echo mage wang zhang | ( read X Y Z; echo $X $Y $Z )
mage wang zhang
[root@centos8 ~]#echo mage wang zhang | while read X Y Z; do echo $X $Y $Z;done
mage wang zhang

范例:
cat while_read_diskcheck.sh
#!/bin/bash
WARNING=80
MAIL=root@wangxiaochun.com
df |sed -nr ?"/^\/dev\/sd/s#^([^ ]+) .* ([0-9]+)%.*#\1 \2#p"|while read DEVICE
USE;do
    if [ $USE -gt $WARNING ] ;then
??      echo "$DEVICE will be full,use:$USE" | mail -s "DISK WARNING" $MAIL
    fi
done

范例:
cat while_read_check_dos.sh
#!/bin/bash
lastb | sed -rn ‘/ssh:/s@.* ([0-9.]{1,3}{3}[0-9]{1,3}) .*@\1@p‘|sort |uniq -c
|while read count ip ;do
    if [ $count -gt 3 ];then
        iptables -A INPUT -s $ip -j REJECT
    fi
done

范例:查看/sbin/nologin的shell类型的用户名和UID
cat while_read_passwd.sh
#!/bin/bash
while read line ;do

if [[ "$line" =~ /sbin/nologin$ ]] ;then
???????? echo $line | cut -d: -f1,3
???? fi ??????????????????????????????????
???????????????
done < /etc/passwd

范例:单词文件
[root@centos6 ~]#wc -l /usr/share/dict/linux.words
479829 /usr/share/dict/linux.words

1.8 select 循环与菜单
格式:
select NAME [in WORDS ... ;] do COMMANDS; done
select variable in list ;do
    循环体命令
done

说明:
select 循环主要用于创建菜单,按数字顺序排列的菜单项显示在标准错误上,并显示 PS3 提示
符,等待用户输入
用户输入菜单列表中的某个数字,执行相应的命令
用户输入被保存在内置变量 REPLY 中
select 是个无限循环,因此要用 break 命令退出循环,或用 exit 命令终止脚本。也可以按 ctrl+c
退出循环
select 经常和 case 联合使用
与 for 循环类似,可以省略 in list,此时使用位置参量

范例:
cat select.sh
#!/bin/bash
sum=0
PS3="请点菜(1-6): "
select MENU in 北京烤鸭 佛跳墙 小龙虾 羊蝎子 火锅 点菜结束;do
case $REPLY in
1)
    echo $MENU 价格是 100
    let sum+=100
;;
2)
    echo $MENU 价格是 88
    let sum+=88
;;
3)
    echo $MENU价格是 66
    let sum+=66
;;
4)
    echo $MENU 价格是 166
    let sum+=166
;;
5)
    echo $MENU 价格是 200
    let sum+=200
;;
6)
    echo "点菜结束,退出"
    break
    ;;
*)
    echo "点菜错误,重新选择"
    ;;
esac
done
echo "总价格是: $sum

二级标题

2 函数介绍
函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程
它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一
部分
函数和shell程序比较相似,区别在于
Shell程序在子Shell中运行,而Shell函数在当前Shell中运行。因此在当前Shell中,函数可对shell中变量
进行修改

2.1 管理函数
函数由两部分组成:函数名和函数体
帮助参看:help function
    
    2.1.1 定义函数
    #语法一:
    func_name (){
        ...函数体...
    }
    #语法二:
    function func_name {
        ...函数体...
    }
    #语法三:
    function func_name () {
        ...函数体...
    }
    
    2.1.2 查看函数
    #查看当前已定义的函数名
    declare -F
    #查看当前已定义的函数定义
    declare -f
    #查看指定当前已定义的函数名
    declare -f func_name
    #查看当前已定义的函数名定义
    declare -F func_name
    
    
    2.1.3 删除函数
    格式:
    unset func_name

2.2 函数调用
函数的调用方式
    可在交互式环境下定义函数
    可将函数放在脚本文件中作为它的一部分
    可放在只包含函数的单独文件中
调用:函数只有被调用才会执行,通过给定函数名调用函数,函数名出现的地方,会被自动替换为函数
代码
函数的生命周期:被调用时创建,返回时终止
    
    2.2.1 交互式环境调用函数
    交互式环境下定义和使用函数
    范例:
    [root@centos8 ~]#dir() {
    > ls -l
    > }
    [root@centos8 ~]#dir
    total 4
    -rw-------. 1 root root 1559 Nov 7 19:33 anaconda-ks.cfg 
    
    2.2.2 在脚本中定义及使用函数
    函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现它后才能使用,调用
    函数仅使用其函数名即可

    root@centos8 ~]#cat func1.sh
    #!/bin/bash
    #name:func1
    hello(){
        echo "Hello there today‘s date is `date +%F`"
    }
    echo "now going to the function hello"
    hello
    echo "back from the function"
    [root@centos8 ~]#./func1.sh
    now going to the function hello
    Hello there today‘s date is 2019-12-18
    back from the function

    范例:
    cat reset.sh
    #!/bin/bash
    disable_selinux(){
        sed -i.bak ‘s/SELINUX=enforcing/SELINUX=disabled/‘ /etc/selinux/config
        echo "SElinux已禁用,重新启动后才可生效"
    }
    disable_firewall(){
        systemctl disable --now firewalld &> /dev/null
        echo "防火墙已禁用"
    }
        set_ps1() {
        echo "PS1=‘\[\e[1;35m\][\u@\h \W]\\$\[\e[0m\]‘" > /etc/profile.d/reset.sh
        echo "提示符已修改成功,请重新登录生效"
    }
    set_eth(){
        sed -i.bak ?‘/GRUB_CMDLINE_LINUX=/s#"$# net.ifnames=0"#‘ /etc/default/grub
        grub2-mkconfig -o /boot/grub2/grub.cfg &> /dev/null
        echo "网络名称已修改成功,请重新启动才能生效"
    }
    PS3="请选择相应的编号(1-6): "
    MENU=‘
    禁用SELinux
    关防火墙
    修改提示符
    修改网卡名
    以上全实现
    退出
    ‘
    select M in $MENU ;do
    case $REPLY in
    1)
        disable_selinux
        ;;
    2)
        disable_firewall
        ;;
    3)
        set_ps1
        ;;
    4)
        set_eth
        ;;
    5)
        ???disable_selinux
        disable_firewall
        set_ps1
        set_eth
        ;;
    6)
        break
        ;;
    *)
        echo "请输入正确的数字"
    esac
    done
    
    2.2.3 使用函数文件
    可以将经常使用的函数存入一个单独的函数文件,然后将函数文件载入shell,再进行调用函数
    文件名可任意选取,但最好与相关任务有某种联系,例如:functions
    一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用delcare -f 或set 命令查看所有
    定义的函数,其输出列表包括已经载入shell的所有函数
    若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件
    实现函数文件的过程:
    1. 创建函数文件,只存放函数的定义
    2. 在shell脚本或交互式shell中调用函数文件,格式如下:
    . filename 或 source ?filename 

    范例:
    [root@centos8 ~]#cat functions
    #!/bin/bash
    #functions
    hello(){
    ?  ?echo Run hello Function
    }
    hello2(){
    ?  ?echo Run hello2 Function
    }
    [root@centos8 ~]#. functions
    [root@centos8 ~]#hello
    Run hello Function
    [root@centos8 ~]#hello2
    Run hello2 Function
    [root@centos8 ~]#declare -f hello hello2
    hello ()
    {
    ??  echo Run hello Function
    }
    hello2 ()
    {
    ?  ?echo Run hello2 Function
    }

    范例:
    [root@centos8 script40]#cat reset.sh
    #!/bin/bash
    . /etc/init.d/functions
    disable_selinux(){
        sed -i.bak ‘s/SELINUX=enforcing/SELINUX=disabled/‘ /etc/selinux/config
    action "SElinux已禁用,重新启动后才可生效"
    }
    disable_firewall(){
        systemctl disable --now firewalld &> /dev/null
        action "防火墙已禁用"
    }
    set_ps1() {
        echo "PS1=‘\[\e[1;35m\][\u@\h \W]\\$\[\e[0m\]‘" > /etc/profile.d/reset.sh
        action "提示符已修改成功,请重新登录生效"
    }
    set_eth(){
        sed -i.bak ?‘/GRUB_CMDLINE_LINUX=/s#"$# net.ifnames=0"#‘ /etc/default/grub
        grub2-mkconfig -o /boot/grub2/grub.cfg &> /dev/null
        action "网络名称已修改成功,请重新启动才能生效"
    }
    PS3="请选择相应的编号(1-6): "
    MENU=‘
    禁用SELinux
    关防火墙
    修改提示符
    修改网卡名
    以上全实现
    退出
    ‘
    select M in $MENU ;do
    case $REPLY in
    1)
        disable_selinux
        ;;
    2)
        disable_firewall
        ;;
    3)
        set_ps1
        ;;
    4)
        set_eth
        ;;
    5)
        ?disable_selinux
        disable_firewall
        set_ps1
        set_eth
        ;;
    6)
        break
        ;;
    *)
        echo "请输入正确的数字"
    esac
    done  
2.3 函数返回值
函数的执行结果返回值:
使用echo等命令进行输出
函数体中调用命令的输出结果
函数的退出状态码:
默认取决于函数中执行的最后一条命令的退出状态码
自定义退出状态码,其格式为:
return 从函数中返回,用最后状态命令决定返回值
return 0 无错误返回
return 1-255 有错误返回

2.4 环境函数
类拟于环境变量,也可以定义环境函数,使子进程也可使用父进程定义的函数
定义环境函数:
export -f function_name
declare -xf function_name

2.5 函数参数
函数可以接受参数:
传递参数给函数:在函数名后面以空白分隔给定参数列表即可,如:testfunc arg1 arg2 ...
在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*, $#等特殊变量

2.6 函数变量
变量作用域:
普通变量:只在当前shell进程有效,为执行脚本会启动专用子shell进程;因此,本地变量的作用
范围是当前shell脚本程序文件,包括脚本中的函数
环境变量:当前shell和子shell有效
本地变量:函数的生命周期;函数结束时变量被自动销毁
注意:
如果函数中定义了普通变量,且名称和局部变量相同,则使用本地变量
由于普通变量和局部变量会冲突,建议在函数中只使用本地变量

在函数中定义本地变量的方法
local NAME=VALUE


2.7 函数递归
函数递归:函数直接或间接调用自身,注意递归层数,可能会陷入死循环
递归示例:
阶乘是基斯顿·卡曼于 1808 年发明的运算符号,是数学术语,一个正整数的阶乘(factorial)是所有小
于及等于该数的正整数的积,并且0和1的阶乘为1,自然数n的阶乘写作n!
n!=1×2×3×...×n
阶乘亦可以递归方式定义:0!=1,n!=(n-1)!×n
n!=n(n-1)(n-2)...1
n(n-1)! = n(n-1)(n-2)!
范例:fact.sh
#!/bin/bash
#
fact() {
?? if [ $1 -eq 0 -o $1 -eq 1 ]; then
       echo 1
??else
       echo $[$1*$(fact $[$1-1])]
?? fi
}
fact $1

fork 炸弹是一种恶意程序,它的内部是一个不断在 fork 进程的无限循环,实质是一个简单的递归程
序。由于程序是递归的,如果没有任何限制,这会导致这个简单的程序迅速耗尽系统里面的所有资源
参考:https://en.wikipedia.org/wiki/Fork_bomb
函数实现

:(){ :|:& };:
bomb() { bomb | bomb & }; bomb

脚本实现
cat Bomb.sh
#!/bin/bash
./$0|./$0&

技术分享图片

二级标题

3 其它脚本相关工具

3.1 信号捕捉 trap
trap ‘触发指令‘ 信号
进程收到系统发出的指定信号后,将执行自定义指令,而不会执行原操作
trap ‘‘ 信号
忽略信号的操作
trap ‘-‘ 信号
恢复原信号的操作
trap -p
列出自定义信号操作
trap finish EXIT
当脚本退出时,执行finish函数
范例:
#!/bin/bash
trap ‘echo "Press ctrl+c"‘ int quit
trap -p
for((i=0;i<=10;i++))
do
???sleep 1
???echo $i
done
trap ‘‘ int
trap -p
for((i=11;i<=20;i++))
do
???sleep 1
???echo $i
done
trap ‘-‘ int
trap -p
for((i=21;i<=30;i++))
do
???sleep 1
???echo $i
done

范例:
[root@centos8 script40]#cat trap_exit.sh
#!/bin/bash
finish(){
    echo finish| tee -a /root/finish.log
}
trap finish exit
while true ;do
    echo running
    sleep 1
done

3.2 创建临时文件 mktemp
mktemp 命令用于创建并显示临时文件,可避免冲突
格式
mktemp [OPTION]... [TEMPLATE]
说明:TEMPLATE: filenameXXX,X至少要出现三个
常见选项:
-d 创建临时目录
-p DIR或--tmpdir=DIR 指明临时文件所存放目录位置
范例:
[root@centos8 data]#mktemp
/tmp/tmp.UogGCumh8C
mktemp /tmp/testXXX
tmpdir=`mktemp –d /tmp/testdirXXX`
mktemp --tmpdir=/testdir testXXXXXX

范例:
[root@centos8 ~]#cat /data/scripts/rm.sh
#!/bin/bash
DIR=`mktemp -d /tmp/trash-$(date +%F_%H-%M-%S)XXXXXX`
mv $* $DIR
echo $* is move to $DIR
[root@centos8 ~]#alias rm=/data/scripts/rm.sh




3.3 安装复制文件 install
install 功能相当于cp,chmod,chown,chgrp 等相关工具的集合
install命令格式:
install [OPTION]... [-T] SOURCE DEST 单文件
install [OPTION]... SOURCE... DIRECTORY
install [OPTION]... -t DIRECTORY SOURCE...
install [OPTION]... -d DIRECTORY...创建空目录

选项:
-m MODE,默认755
-o OWNER
-g GROUP
-d DIRNAME 目录

范例:
install -m 700 -o wang -g admins srcfile desfile
install –m 770 –d /testdir/installdir
[root@centos8 ~]#install -m 700 -o mage -g daemon -d /data/testdir
[root@centos8 ~]#ll -d /data/testdir
drwx------ 2 mage daemon 6 Apr 29 15:09 /data/testdir
    
3.4 交互式转化批处理工具 expect
expect 是由Don Libes基于 Tcl( Tool Command Language )语言开发的,主要应用于自动化交互式
操作的场景,借助 expect 处理交互的命令,可以将交互过程如:ssh登录,ftp登录等写在一个脚本
上,使之自动化完成。尤其适用于需要对多台服务器执行相同操作的环境中,可以大大提高系统管理人
员的工作效率
expect 语法:
expect [选项] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]

常见选项:
-c:从命令行执行expect脚本,默认expect是交互地执行的
-d:可以输出输出调试信息
示例:
expect ?-c ‘expect "\n" {send "pressed enter\n"}‘
expect ?-d ssh.exp

expect中相关命令
spawn 启动新的进程
expect 从进程接收字符串
send 用于向进程发送字符串
interact 允许用户交互
exp_continue 匹配多个字符串在执行动作后加此命令

expect最常用的语法(tcl语言:模式-动作)
单一分支模式语法:
[root@centos8 test]#expect
expect1.1> expect "hi" {send "You said hi\n"}
hahahixixi
You said hi

匹配到hi后,会输出“you said hi”,并换行
多分支模式语法:
[root@centos8 test]#expect
expect1.1> expect "hi" { send "You said hi\n" } "hehe" { send "Hehe yourself\n"
} "bye" { send "Good bye\n" }
hehe
Hehe yourself
expect1.2> expect "hi" { send "You said hi\n" } "hehe" { send "Hehe yourself\n"
} "bye" { send "Good bye\n" }
bye
Good bye
expect1.3> expect "hi" { send "You said hi\n" } "hehe" { send "Hehe yourself\n"
} "bye" { send "Good bye\n" }
hi
You said hi
expect1.4>

匹配hi,hello,bye任意字符串时,执行相应输出。等同如下:
expect {
    "hi" { send "You said hi\n"}
    "hehe" { send "Hehe yourself\n"}
    "bye" { send " Good bye\n"}
}
[root@centos8 ~]#expect
expect1.1> expect {
+> "hi" { send "You said hi\n"}
+> "hehe" { send "Hehe yourself\n"}
+> "bye" { send " Good bye\n"}
+> }
bye
Good bye
expect1.2>

范例1:
#!/usr/bin/expect
spawn scp /etc/fstab 10.0.0.7:/data
expect {
??? "yes/no" { send "yes\n";exp_continue }
??? "password" { send "magedu\n" }
}
expect eof

范例2:
#!/usr/bin/expect
spawn ssh 10.0.0.7
expect {
???"yes/no" { send "yes\n";exp_continue }
???"password" { send "magedu\n" }
}
interact

范例3:expect 变量
#!/usr/bin/expect
set ip 10.0.0.7
set user root
set password magedu
set timeout 10
spawn ssh $user@$ip
expect {
???"yes/no" { send "yes\n";exp_continue }
???"password" { send "$password\n" }
}
interact

范例4:expect 位置参数
[root@centos8 scripts]#cat expect4
#!/usr/bin/expect
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
spawn ssh $user@$ip
expect {
???"yes/no" { send "yes\n";exp_continue }
???"password" { send "$password\n" }
}
interact
[root@centos8 scripts]#./expect4 10.0.0.7 root magedu
spawn ssh root@10.0.0.7
root@10.0.0.7‘s password:
Last login: Wed Apr 29 15:34:14 2020 from 10.0.0.8
[root@centos7 ~]#exit
logout
Connection to 10.0.0.7 closed.

范例5:expect 执行多个命令
#!/usr/bin/expect
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
set timeout 10
spawn ssh $user@$ip
expect {
???"yes/no" { send "yes\n";exp_continue }
???"password" { send "$password\n" }
}
expect "]#" { send "useradd haha\n" }
expect "]#" { send "echo magedu |passwd --stdin haha\n" }
send "exit\n"
expect eof
#./ssh4.exp 10.0.0.7 root magedu

范例6:shell脚本调用expect
#!/bin/bash
ip=$1
user=$2
password=$3
expect <<EOF
set timeout 20
spawn ssh $user@$ip
expect {
???"yes/no" { send "yes\n";exp_continue }
???"password" { send "$password\n" }
}
expect "]#" { send "useradd hehe\n" }
expect "]#" { send "echo magedu |passwd --stdin hehe\n" }
expect "]#" { send "exit\n" }
expect eof
EOF
#./ssh5.sh 192.168.8.10 root magedu


范例7: shell脚本利用循环调用expect在CentOS和Ubuntu上批量创建用户
#!/bin/bash
NET=10.0.0
user=root
password=magedu
for ID in 6 7 111;do
ip=$NET.$ID
expect <<EOF
set timeout 20
spawn ssh $user@$ip
expect {
????"yes/no" { send "yes\n";exp_continue }
????"password" { send "$password\n" }
}
expect "#" { send "useradd test\n" }
expect "#" { send "exit\n" }
expect eof
EOF
done

范例:
[root@centos8 scripts]#cat expect8.sh
#!/bin/bash
NET=10.0.0
user=root
password=centos
for ID in 202 204 ;do
ip=$NET.$ID
expect <<EOF
set timeout 20
spawn ssh $user@$ip
expect {
????"yes/no" { send "yes\n";exp_continue }
????"password" { send "$password\n" }
}
expect "#" { send "sed -i ‘s/^SELINUX=enforcing/SELINUX=disabled/‘
/etc/selinux/config\n" }
expect "#" { send "setenforce 0\n" }
expect "#" { send "exit\n" }
expect eof
EOF
done

二级标题

4 数组array

4.1 数组介绍
变量:存储单个元素的内存空间
数组:存储多个元素的连续的内存空间,相当于多个变量的集合
数组名和索引
索引的编号从0开始,属于数值索引
索引可支持使用自定义的格式,而不仅是数值格式,即为关联索引,bash4.0版本之后开始支持
bash的数组支持稀疏格式(索引不连续)

4.2 声明数组
#普通数组可以不事先声明,直接使用
declare -a ARRAY_NAME
#关联数组必须先声明,再使用
declare -A ARRAY_NAME

注意:两者不可相互转换

4.3 数组赋值
数组元素的赋值
(1) 一次只赋值一个元素
ARRAY_NAME[INDEX]=VALUE

范例:
weekdays[0]="Sunday"
weekdays[4]="Thursday"

(2) 一次赋值全部元素
ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)

范例:
title=("ceo" "coo" "cto")
num=({0..10})
alpha=({a..g})
file=( *.sh )

(3) 只赋值特定元素
ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)

(4) 交互式数组值对赋值
read -a ARRAY

范例:
[root@centos8 ~]#declare -A course
[root@centos8 ~]#declare -a course
-bash: declare: course: cannot convert associative to indexed array
[root@centos8 ~]#file=( *.sh )
[root@centos8 ~]#declare -A file
-bash: declare: file: cannot convert indexed to associative array

4.4 显示所有数组
显示所有数组:
declare -a


4.5 引用数组
引用数组元素
${ARRAY_NAME[INDEX]}
#如果省略[INDEX]表示引用下标为0的元素

范例:
[root@centos8 ~]#declare -a title=([0]="ceo" [1]="coo" [2]="cto")
[root@centos8 ~]#echo ${title[1]}
coo
[root@centos8 ~]#echo ${title}
ceo
[root@centos8 ~]#echo ${title[2]}
cto
[root@centos8 ~]#echo ${title[3]}

引用数组所有元素
${ARRAY_NAME[*]}
${ARRAY_NAME[@]}

范例:
root@centos8 ~]#echo ${title[@]}
ceo coo cto
[root@centos8 ~]#echo ${title[*]}
ceo coo cto

范例:
[root@centos8 ~]#echo ${#title[*]}
3
4.6 删除数组
删除数组中的某元素,会导致稀疏格式
unset ARRAY[INDEX]

[root@centos8 ~]#echo ${title[*]}
ceo coo cto
[root@centos8 ~]#unset title[1]
[root@centos8 ~]#echo ${title[*]}
ceo cto

删除整个数组
unset ARRAY

范例:
[root@centos8 ~]#unset title
[root@centos8 ~]#echo ${title[*]}
[root@centos8 ~]#

4.7 数组数据处理
数组切片:
${ARRAY[@]:offset:number}
offset #要跳过的元素个数
number #要取出的元素个数
#取偏移量之后的所有元素
{ARRAY[@]:offset}

范例:
[root@centos8 ~]#num=({0..10})
[root@centos8 ~]#echo ${num[*]:2:3}
2 3 4
[root@centos8 ~]#echo ${num[*]:6}
6 7 8 9 10

向数组中追加元素:
ARRAY[${#ARRAY[*]}]=value
ARRAY[${#ARRAY[@]}]=value

范例
[root@centos8 ~]#num[${#num[@]}]=11
[root@centos8 ~]#echo ${#num[@]}
12
[root@centos8 ~]#echo ${num[@]}
0 1 2 3 4 5 6 7 8 9 10 11

4.8 关联数组
declare -A ARRAY_NAME
ARRAY_NAME=([idx_name1]=‘val1‘ [idx_name2]=‘val2‘...)
注意:关联数组必须先声明再调用
范例:
[root@centos8 ~]#name[ceo]=mage
[root@centos8 ~]#name[cto]=wang
[root@centos8 ~]#name[coo]=zhang
[root@centos8 ~]#echo ${name[ceo]}
zhang
[root@centos8 ~]#echo ${name[cto]}
zhang
[root@centos8 ~]#echo ${name[coo]}
zhang
[root@centos8 ~]#echo ${name}
zhang
[root@centos8 ~]#declare -A name
-bash: declare: name: cannot convert indexed to associative array
[root@centos8 ~]#unset name
[root@centos8 ~]#declare -A name
[root@centos8 ~]#name[ceo]=mage
[root@centos8 ~]#name[cto]=wang
[root@centos8 ~]#name[coo]=zhang
[root@centos8 ~]#echo ${name[coo]}
zhang
[root@centos8 ~]#echo ${name[ceo]}
mage
[root@centos8 ~]#echo ${name[cto]}
wang
[root@centos8 ~]#echo ${name[*]}
mage wang zhang

范例:关联数组
[root@centos8 ~]#declare -A student
[root@centos8 ~]#student[name1]=lijun
[root@centos8 ~]#student[name2]=ziqing
[root@centos8 ~]#student[age1]=18
[root@centos8 ~]#student[age2]=16
[root@centos8 ~]#student[gender1]=m
[root@centos8 ~]#student[city1]=nanjing
[root@centos8 ~]#student[gender2]=f
[root@centos8 ~]#student[city2]=anhui
[root@centos8 ~]#student[gender2]=m
[root@centos8 ~]#student[name50]=alice
[root@centos8 ~]#student[name3]=tom
[root@centos8 ~]#for i in {1..50};do echo student[name$i]=${student[name$i]};



4.9 范例
范例:生成10个随机数保存于数组中,并找出其最大值和最小值
#!/bin/bash
declare -i min max
declare -a nums
for ((i=0;i<10;i++));do
    nums[$i]=$RANDOM
    [ $i -eq 0 ] && min=${nums[0]} && ?max=${nums[0]}&& continue
    [ ${nums[$i]} -gt $max ] && max=${nums[$i]}
    [ ${nums[$i]} -lt $min ] && min=${nums[$i]}
done
echo "All numbers are ${nums[*]}"
echo Max is $max
echo Min is $min

范例:编写脚本,定义一个数组,数组中的元素对应的值是/var/log目录下所有以.log结尾的文件;统计
出其下标为偶数的文件中的行数之和
#!/bin/bash
#
declare -a files
files=(/var/log/*.log)
declare -i lines=0
for i in $(seq 0 $[${#files[*]}-1]); do
??if [ $[$i%2] -eq 0 ];then
      let lines+=$(wc -l ${files[$i]} | cut -d‘ ‘ -f1)
??fi
done
echo "Lines: $lines"

二级标题

5字符串处理

5.1 字符串切片
基于偏移量取字符串
#返回字符串变量var的长度
${#var}
#返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,到最后的部分,
offset的取值在0 到 ${#var}-1 之间(bash4.2后,允许为负值)
${var:offset}
#返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,长度为number的部分
${var:offset:number}
#取字符串的最右侧几个字符,取字符串的最右侧几个字符, 注意:冒号后必须有一空白字符
${var: -length}
#从最左侧跳过offset字符,一直向右取到距离最右侧lengh个字符之前的内容,即:掐头去尾
${var:offset:-length}
#先从最右侧向左取到length个字符开始,再向右取到距离最右侧offset个字符之间的内容,注意:-
length前空格
${var: -length:-offset}

范例:
[root@centos8 script40]#str=abcdef我你他
[root@centos8 script40]#echo ${#str}
9
[root@centos8 script40]#echo ${str:2}
cdef我你他
[root@centos8 script40]#echo ${str:2:3}
cde
[root@centos8 script40]#echo ${str:-3}
abcdef我你他
[root@centos8 script40]#echo ${str: -3}
我你他
[root@centos8 script40]#echo ${str:2:-3}
cdef
[root@centos8 script40]#echo ${str: -2:-3}
-bash: -3: substring expression < 0
[root@centos8 script40]#echo ${str: -3:-2}
我
[root@centos8 script40]#echo ${str:-3:-2}
abcdef我你他
[root@centos8 script40]#echo ${str: -3:-2}
我
[root@centos8 script40]#echo ${str: -5:-2}
ef我

基于模式取子串
#其中word可以是指定的任意字符,自左而右,查找var变量所存储的字符串中,第一次出现的word, 删除字
符串开头至第一次出现word字符串(含)之间的所有字符
${var#*word}:
#同上,贪婪模式,不同的是,删除的是字符串开头至最后一次由word指定的字符之间的所有内容
${var##*word}:

范例:
[root@centos8 ~]#file="var/log/messages"
[root@centos8 ~]#echo ${file#*/}
log/messages
[root@centos8 ~]#echo ${file##*/}
messages

#其中word可以是指定的任意字符,功能:自右而左,查找var变量所存储的字符串中,第一次出现的word,
删除字符串最后一个字符向左至第一次出现word字符串(含)之间的所有字符
${var%word*}
#同上,只不过删除字符串最右侧的字符向左至最后一次出现word字符之间的所有字符
${var%%word*}

范例:
[root@centos8 ~]#file="var/log/messages"
[root@centos8 ~]#echo ${file%/*}
var/log
[root@centos8 ~]#echo ${file%%/*}
var

范例:
[root@centos8 ~]#url=http://www.magedu.com:8080
[root@centos8 ~]#echo ${url##*:}
8080
[root@centos8 ~]#echo ${url%%:*}
http



5.2 查找替换
#查找var所表示的字符串中,第一次被pattern所匹配到的字符串,以substr替换之
${var/pattern/substr}
#查找var所表示的字符串中,所有能被pattern所匹配到的字符串,以substr替换之
${var//pattern/substr}
#查找var所表示的字符串中,行首被pattern所匹配到的字符串,以substr替换之
${var/#pattern/substr}
#查找var所表示的字符串中,行尾被pattern所匹配到的字符串,以substr替换之
${var/%pattern/substr}

5.3 查找并删除
#删除var表示的字符串中第一次被pattern匹配到的字符串
${var/pattern}
删除var表示的字符串中所有被pattern匹配到的字符串
${var//pattern}
删除var表示的字符串中所有以pattern为行首匹配到的字符串
${var/#pattern}
删除var所表示的字符串中所有以pattern为行尾所匹配到的字符串
${var/%pattern}

5.4 字符大小写转换
#把var中的所有小写字母转换为大写
${var^^}
#把var中的所有大写字母转换为小写
${var,,}

二级标题

6 高级变量

6.1 高级变量赋值

技术分享图片

范例:
[root@centos8 ~]#title=ceo
[root@centos8 ~]#name=${title-mage}
[root@centos8 ~]#echo $name
ceo
[root@centos8 ~]#title=
[root@centos8 ~]#name=${title-mage}
[root@centos8 ~]#echo $name
[root@centos8 ~]#unset title
[root@centos8 ~]#name=${title-mage}
[root@centos8 ~]#echo $name
mage

范例:
[root@centos8 ~]#title=ceo
[root@centos8 ~]#name=${title:-mage}
[root@centos8 ~]#echo $name
ceo
[root@centos8 ~]#title=
[root@centos8 ~]#name=${title:-mage}
[root@centos8 ~]#echo $name
mage
[root@centos8 ~]#unset title
[root@centos8 ~]#name=${title:-mage}
[root@centos8 ~]#echo $name
mage

6.2 高级变量用法-有类型变量
Shell变量一般是无类型的,但是bash Shell提供了declare和typeset两个命令用于指定变量的类型,两
个命令是等价的
declare [选项] 变量名
选项:
-r 声明或显示只读变量
-i 将变量定义为整型数
-a 将变量定义为数组
-A 将变量定义为关联数组
-f 显示已定义的所有函数名及其内容
-F 仅显示已定义的所有函数名
-x 声明或显示环境变量和函数,相当于export
-l 声明变量为小写字母 declare –l var=UPPER
-u 声明变量为大写字母 declare –u var=lower

6.3 变量间接引用
   
    6.3.1 eval命令
    eval命令将会首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实
    现其功能的变量,该命令对变量进行两次扫描
    范例:
    [root@centos8 ~]# CMD=whoami
    [root@centos8 ~]# echo $CMD
    whoami
    [root@centos8 ~]# eval $CMD
    root
    [root@centos8 ~]# n=10 ???
    [root@centos8 ~]# echo {0..$n} ?
    {0..10}
    [root@centos8 ~]# eval echo {0..$n}
    0 1 2 3 4 5 6 7 8 9 10
    [root@centos8 ~]#for i in `eval echo {1..$n}` ;do echo i=$i ;done
    i=1
    i=2
    i=3
    i=4
    i=5
    i=6
    i=7
    i=8
    i=9
    i=10
    [root@centos8 ~]#i=1
    [root@centos8 ~]#j=a
    [root@centos8 ~]#$j$i=hello
    -bash: a1=hello: command not found
    [root@centos8 ~]#eval $j$i=hello
    [root@centos8 ~]#echo $j$i
    a1
    6.3.2 间接变量引用
    如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量引用
    variable1的值是variable2,而variable2又是变量名,variable2的值为value,间接变量引用是指通过
    variable1获得变量值value的行为

    variable1=variable2
    variable2=value

    bash Shell提供了两种格式实现间接变量引用
    eval tempvar=\$$variable1
    tempvar=${!variable1}

    范例
    [root@centos8 ~]#ceo=name
    [root@centos8 ~]#name=mage
    [root@centos8 ~]#echo $ceo
    name
    [root@centos8 ~]#echo $$ceo
    33722ceo
    [root@centos8 ~]#echo $BASHPID
    33722
    [root@centos8 ~]#echo \$$ceo
    $name
    [root@centos8 ~]#eval tmp=\$$ceo
    [root@centos8 ~]#echo $tmp
    mage
    [root@centos8 ~]#echo ${!ceo}
    mage
    [root@server ~]# NAME=wangxiaochun
    [root@server ~]# N=NAME
    [root@server ~]# N1=${!N}
    [root@server ~]# echo $N1
    wangxiaochun
    [root@server ~]# eval N2=\$$N
    [root@server ~]# echo $N2
    wangxiaochun
    
    6.3.3 变量引用reference
    [root@centos8 ~]#cat test.sh
    #!/bin/bash
    ceo=mage
    title=ceo
    declare -n ref=$title
    [ -R ref ] && echo reference ????????????????????????
    ?????echo $ref
    ceo=wang
    echo $ref
    [root@centos8 ~]#bash test.sh
    reference
    mage
    wang

shell脚本进阶

原文:https://www.cnblogs.com/zhaihongyu/p/12861837.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!