Ubuntu系统网络配置及shell脚本编程之函数数组等用法详解

1、Ubuntu系统网络配置总结

1.1 主机名

修改主机名

root@ubuntu20:/home/dong# hostname
ubuntu20
root@ubuntu20:/home/dong# hostnamectl set-hostname ubuntu20.magedu.cn
root@ubuntu20:/home/dong# hostname
ubuntu20.magedu.cn
root@ubuntu20:/home/dong# cat /etc/hostname
ubuntu20.magedu.cn
root@ubuntu20:/home/dong# echo $HOSTNAME        #不能立即生效,重新登录后生效
ubuntu20
root@ubuntu20:/home/dong# logout
bash: logout: not login shell: use `exit'
root@ubuntu20:/home/dong# exit
exit
dong@ubuntu20:~$ su
Password:
root@ubuntu20:/home/dong# echo $HOSTNAME
ubuntu20.magedu.cn

1.2 网卡名称

默认ubuntu的网卡名称和 CentOS 7 类似,如:ens33,ens38等

修改网卡名称为传统命名方式:

#查看网卡默认名称为ens系列
root@ubuntu20:~# ip a |grep ens
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    inet 192.168.100.180/24 brd 192.168.100.255 scope global ens33
#查看配置文件
root@ubuntu20:~# cat /etc/default/grub
GRUB_DEFAULT=0
GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=0
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="maybe-ubiquity"
GRUB_CMDLINE_LINUX=""

1)修改配置文件GRUB_CMDLINE_LINUX
root@ubuntu20:~# sed -i.bak '/^GRUB_CMDLINE_LINUX=/s#"$#net.ifnames=0"#' /etc/default/grub
2)生成新的grub.cfg文件
root@ubuntu20:~# grub-mkconfig -o /boot/grub/grub.cfg
Sourcing file `/etc/default/grub'
Sourcing file `/etc/default/grub.d/init-select.cfg'
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-5.4.0-42-generic
Found initrd image: /boot/initrd.img-5.4.0-42-generic
done
#或者
root@ubuntu20:~# update-grub
root@ubuntu20:~# grep net.ifnames /boot/grub/grub.cfg
        linux   /vmlinuz-5.4.0-42-generic root=/dev/mapper/ubuntu--vg-ubuntu--lv ro net.ifnames=0 maybe-ubiquity
                linux   /vmlinuz-5.4.0-42-generic root=/dev/mapper/ubuntu--vg-ubuntu--lv ro net.ifnames=0 maybe-ubiquity
                linux   /vmlinuz-5.4.0-42-generic root=/dev/mapper/ubuntu--vg-ubuntu--lv ro recovery nomodeset net.ifnames=0
dong@ubuntu20:~$

#重启后生效
root@ubuntu20:~# reboot

3)重启后,eth0无ip地址,替换原配置的接口名称
root@ubuntu20:~# sed -i 's/ens33/eth0' /etc/netplan/00-installer-config.yaml
root@ubuntu20:~# netplan apply  #生效
root@ubuntu20:~# cat /etc/netplan/00-installer-config.yaml
# This is the network config written by 'subiquity'
network:
  ethernets:
    eth0:
    # dhcp: no
      addresses: [192.168.100.180/24]
      optional: true
      gateway4: 192.168.100.2
      nameservers:
               addresses: [192.168.100.2]
  version: 2
root@ubuntu20:~# ip a |grep eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    inet 192.168.100.180/24 brd 192.168.100.255 scope global eth0

1.3 Ubuntu网卡配置

官网文档:
帮助文档:https://help.ubuntu.com/
https://help.ubuntu.com/lts/serverguide/network-configuration.html.zh-CN
网络配置文档:https://ubuntu.com/server/docs/network-configuration

网络配置文件:yaml格式
1)- 列表:表示多个成员或[ ]
2)键(变量名):值
3)不同层级有缩进,而且统一

1.3.1 配置自动获取IP

范例:

root@ubuntu20:~# cat /etc/netplan/00-installer-config.yaml
# This is the network config written by 'subiquity'
network:
  ethernets:
    eth0:
      dhcp4: yes
      #addresses: [192.168.100.180/24]
      #optional: true
      #gateway4: 192.168.100.2
      #nameservers:
      #         addresses: [192.168.100.2]
  version: 2

#修改网卡配置文件后需执行命令生效:
root@ubuntu20:~# netplan apply

root@ubuntu20:~# ip a|grep eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    inet 192.168.100.128/24 brd 192.168.100.255 scope global dynamic eth0

1.3.2 配置静态IP

范例:

root@ubuntu20:~# cat /etc/netplan/00-installer-config.yaml
# This is the network config written by 'subiquity'
network:
  ethernets:
    eth0:
      #dhcp4: yes
      addresses: [192.168.100.128/24,192.168.100.180/24]
      optional: true
      gateway4: 192.168.100.2
      nameservers:
              #search:[magedu.com,magedu.org]
        addresses: [192.168.100.2]
  version: 2
#或者用下面两行,两种格式不能混用
      addresses:
      - 192.168.100.128/24
      - 192.168.100.180/24
root@ubuntu20:~# ip a|grep eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:02:81:56 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.128/24 brd 192.168.100.255 scope global eth0
    inet 192.168.100.180/24 brd 192.168.100.255 scope global secondary eth0

查看ip和gateway

root@ubuntu20:/etc/apt# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:02:81:56 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.128/24 brd 192.168.100.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet 192.168.100.180/24 brd 192.168.100.255 scope global secondary eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe02:8156/64 scope link
       valid_lft forever preferred_lft forever

查看路由表route

#1)执行route -n
root@ubuntu20:~# route -n   #route没有安装,需安装net-tools文件
Command 'route' not found, but can be installed with:
apt install net-tools
root@ubuntu20:~# apt install net-tools
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
  net-tools
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 196 kB of archives.
After this operation, 864 kB of additional disk space will be used.
Err:1 http://mirrors.aliyun.com focal/main amd64 net-tools amd64 1.60+git20180626.aebd88e-1ubuntu1
  404  Not Found [IP: 111.62.129.238 80]
E: Failed to fetch http://mirrors.aliyun.com/pool/main/n/net-tools/net-tools_1.60+git20180626.aebd88e-1ubuntu1_amd64.deb  404  Not Found [IP: 111.62.129.238 80]
E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?

#2)因aliyun源无法连通,更换为清华源
root@ubuntu20:/# cd /etc/apt
root@ubuntu20:/etc/apt# cp sources.list sources.list.bak
#编辑source.list,复制清华源
root@ubuntu20:/etc/apt# cat sources.list
# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse

#3)更新
root@ubuntu20:/etc/apt# apt update
#4)安装net-tools工具
root@ubuntu20:/etc/apt# apt install net-tools
#5)查看路由表
root@ubuntu20:/etc/apt# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.100.2   0.0.0.0         UG    0      0        0 eth0
192.168.100.0   0.0.0.0         255.255.255.0   U     0      0        0 eth0

查看DNS

dong@ubuntu20:~$ ll /etc/resolv.conf
lrwxrwxrwx 1 root root 39 Jul 31  2020 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
dong@ubuntu20:~$ systemd-resolve --status
Global
       LLMNR setting: no
MulticastDNS setting: no
  DNSOverTLS setting: no
      DNSSEC setting: no
    DNSSEC supported: no
          DNSSEC NTA: 10.in-addr.arpa
                      16.172.in-addr.arpa
                      168.192.in-addr.arpa
                      17.172.in-addr.arpa
                      18.172.in-addr.arpa
                      19.172.in-addr.arpa
                      20.172.in-addr.arpa
                      21.172.in-addr.arpa
                      22.172.in-addr.arpa
                      23.172.in-addr.arpa
                      24.172.in-addr.arpa
                      25.172.in-addr.arpa
                      26.172.in-addr.arpa
                      27.172.in-addr.arpa
                      28.172.in-addr.arpa
                      29.172.in-addr.arpa
                      30.172.in-addr.arpa
                      31.172.in-addr.arpa
                      corp
                      d.f.ip6.arpa
                      home
                      internal
                      intranet
                      lan
                      local
                      private
                      test

Link 2 (eth0)
      Current Scopes: DNS
DefaultRoute setting: yes
       LLMNR setting: yes
MulticastDNS setting: no
  DNSOverTLS setting: no
      DNSSEC setting: no
    DNSSEC supported: no
  Current DNS Server: 192.168.100.2
         DNS Servers: 192.168.100.2

1.3.3 配置多⽹卡静态IP和静态路由

1、多个网卡配置都在一个文件

root@ubuntu20:~# cat /etc/netplan/00-installer-config.yaml
# This is the network config written by 'subiquity'
network:
  ethernets:
    eth0:
      addresses:
      - 192.168.100.128/24
      optional: true
      gateway4: 192.168.100.2
      nameservers:
              #search:[magedu.com,magedu.org]
        addresses: [192.168.100.2]
  version: 2
  ethernets:
    eth1:
      addresses: [172.16.100.128/24]
      #gateway4: 172.16.100.1
      #nameservers:
      #  addresses: [223.5.5.5,180.76.76.76]
      routes:
      - to: 10.10.100.0/24
        via: 172.16.100.1
      - to: 172.18.100.0/24
        via: 172.16.100.1
      - to: 172.19.100.0/24
        via: 172.16.100.1

root@ubuntu20:~# netplan apply
root@ubuntu20:~# ip a|grep eth
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:02:81:56 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.128/24 brd 192.168.100.255 scope global eth0
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:02:81:60 brd ff:ff:ff:ff:ff:ff
    inet 172.16.100.128/24 brd 172.16.100.255 scope global eth1

root@ubuntu20:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.100.2   0.0.0.0         UG    0      0        0 eth0
10.10.100.0     172.16.100.1    255.255.255.0   UG    0      0        0 eth1
172.16.100.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1
172.18.100.0    172.16.100.1    255.255.255.0   UG    0      0        0 eth1
172.19.100.0    172.16.100.1    255.255.255.0   UG    0      0        0 eth1
192.168.100.0   0.0.0.0         255.255.255.0   U     0      0        0 eth0

2、每个网卡各自一个配置文件

root@ubuntu20:~# cat /etc/netplan/00-installer-config.yaml
# This is the network config written by 'subiquity'
network:
  ethernets:
    eth0:
      addresses:
      - 192.168.100.128/24
      optional: true
      gateway4: 192.168.100.2
      nameservers:
              #search:[magedu.com,magedu.org]
        addresses: [192.168.100.2]
  version: 2

root@ubuntu20:~# cat /etc/netplan/01-eth1-config.yaml
# This is the network config written by 'subiquity'
network:
  version: 2
  ethernets:
    eth1:
      addresses: [172.16.100.128/24]
      #gateway4: 172.16.100.1
      nameservers:
        addresses: [223.5.5.5,180.76.76.76]
      routes:
      - to: 10.10.100.0/24
        via: 172.16.100.1
      - to: 172.18.100.0/24
        via: 172.16.100.1
      - to: 172.19.100.0/24
        via: 172.16.100.1
root@ubuntu20:~# netplan apply
root@ubuntu20:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:02:81:56 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.128/24 brd 192.168.100.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe02:8156/64 scope link
       valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:02:81:60 brd ff:ff:ff:ff:ff:ff
    inet 172.16.100.128/24 brd 172.16.100.255 scope global eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe02:8160/64 scope link
       valid_lft forever preferred_lft forever
root@ubuntu20:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.100.2   0.0.0.0         UG    0      0        0 eth0
10.10.100.0     172.16.100.1    255.255.255.0   UG    0      0        0 eth1
172.16.100.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1
172.18.100.0    172.16.100.1    255.255.255.0   UG    0      0        0 eth1
172.19.100.0    172.16.100.1    255.255.255.0   UG    0      0        0 eth1
192.168.100.0   0.0.0.0         255.255.255.0   U     0      0        0 eth0
root@ubuntu20:~#

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 () {
...函数体...
}

定义函数范例:函数定义和调用在一个脚本中

1)创建一个关闭防火墙和selinux的函数
[root@repo-client scripts]# cat all_functions
disable_firewall_selinux () {
        systemctl stop firewalld
        systemctl disable firewalld
        sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
        setenforce 0
}
disable_firewall_selinux        #调用函数
#原来的selinux和firewall状态
[root@repo-client scripts]# cat /etc/selinux/config
SELINUX=enforcing
[root@repo-client scripts]# systemctl status firewalld
   Active: active (running) since Tue 2021-03-23 13:52:58 CST; 9min ago
#执行函数,并查看结果
[root@repo-client scripts]# . all_functions
[root@repo-client scripts]# systemctl status firewalld
   Active: inactive (dead)
[root@repo-client scripts]# cat /etc/selinux/config
SELINUX=disabled

2)创建centos7统一的repo
[root@repo-client scripts]# cat all_functions
yum_repo () {
    cd /etc/yum.repos.d/
    mkdir backup -p
    mv *.repo backup
    cat > base.repo <<EOF

[BaseOS-aliyun]
name=centos7 repo-server BaseOS by aliyun
baseurl=https://mirrors.aliyun.com/centos/7/os/x86_64/
gpgcheck=0

[epel]
name=centos7 repo-server epel
baseurl=https://mirrors.aliyun.com/epel/7/x86_64/
gpgcheck=0

[extras]
name=centos7 repo-server extras
baseurl=https://mirrors.aliyun.com/centos/7/extras/x86_64/
gpgcheck=0
EOF
}
yum_repo

#查看原来的yum.repos.d目录中的文件
[root@repo-client yum.repos.d]# ls
backup  centos7-client.repo  Centos-7.repo  CentOS-Base.repo
[root@repo-client scripts]# . all_functions
mv: overwrite ‘backup/CentOS-Base.repo’? y
[root@repo-client yum.repos.d]# ll
total 4
drwxr-xr-x. 2 root root 235 Mar 23 14:20 backup
-rw-r--r--. 1 root root 512 Mar 23 14:20 base.repo

[root@repo-client yum.repos.d]# yum repolist
repo id               repo name                                     status
BaseOS-aliyun         centos7 repo-server BaseOS by aliyun          10,072
epel                  centos7 repo-server epel                      13,564
extras                centos7 repo-server extras                    460
repolist: 24,096

范例:函数定义和调用分开

1)函数定义在一个统一的脚本文件中,如all_functions;
[root@repo-client scripts]# cat all_functions
#1)停止防火墙和selinux的函数
disable_firewall_selinux () {
        systemctl stop firewalld
        systemctl disable firewalld
        sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
        setenforce 0
}
#2)定义yum源的函数
yum_repo () {
...同上...省略
}
2)调用函数时,编写相应的sh脚本,如只需要关闭防火墙和selinux,就编写这样一个脚本,就不执行其他的函数了
[root@repo-client scripts]# cat dis_firewall_selinux.sh
#!/bin/bash
#调用函数库
. all_functions
#关闭防火墙和selinux
disable_firewall_selinux
3)测试执行
[root@repo-client scripts]# . dis_firewall_selinux.sh

范例:安装常用软件的函数

1)该函数也定义在统一的脚本文件all_functions中
[root@repo-client scripts]# cat all_functions
#1)关闭防火墙和selinux
disable_firewall_selinux () {
...省略...
}
#2)统一yum源仓库
yum_repo () {
...省略...
}
#3)安装常用软件工具
install_packages () {
PACKAGES="
vim
tree
autofs
net-tools
gcc
make
autoconf
pcre
pcre-devel
openssl
openssl-devel
vim
lrzsz
tmux
lsof
tcpdump
wget
iotop
"
for i in $PACKAGES; do
        rpm -q $i &> /dev/null || yum -q -y install $i
done
}
2、编写安装脚本,测试
[root@repo-client scripts]# rpm -q vim lrzsz lsof gcc
package vim is not installed
package lrzsz is not installed
package lsof is not installed
package gcc is not installed
[root@repo-client scripts]# . install_packages.sh
...安装过程省略...
[root@repo-client scripts]# rpm -q vim lrzsz lsof gcc
package vim is not installed
lrzsz-0.12.20-36.el7.x86_64
lsof-4.87-6.el7.x86_64
gcc-4.8.5-44.el7.x86_64

范例:调用函数的简易运维菜单脚本

[root@centos7 scripts]# cat fun_menu.sh
#!/bin/bash
. all_functions
PS3="请选择运维编号(1-4):"
select MENU in 关闭防火墙和selinux 配置yum仓库 安装常用软件包 退出; do
    case $REPLY in
    1)
        disable_firewall_selinux
        ;;
    2)
        yum_repo
        ;;
    3)
        install_packages
        ;;
    4)
        exit
        ;;
    *)
        echo "输入错误,请重新输入"
        ;;
    esac
done

[root@centos7 scripts]# bash fun_menu.sh
1) 关闭防火墙和selinux  3) 安装常用软件包
2) 配置yum仓库          4) 退出
请选择运维编号(1-4):1
setenforce: SELinux is disabled
请选择运维编号(1-4):2
请选择运维编号(1-4):4
[root@centos7 scripts]#

2.1.2 查看函数

#查看当前已定义的函数名
declare -F
#查看当前已定义的函数定义
declare -f
#查看指定当前已定义的函数名
declare -f func_name
#查看当前已定义的函数名定义
declare -F func_name

[root@repo-client scripts]# declare -f
disable_firewall_selinux ()
{
    systemctl stop firewalld;
    systemctl disable firewalld;
    sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config;
    setenforce 0
}
yum_repo ()
{
    ...省略...
}
[root@repo-client scripts]# declare -F
declare -f disable_firewall_selinux
declare -f yum_repo

2.1.3 删除函数

unset func_name     #删除函数

2.2 函数调用

范例1:函数体内执行的变量赋值,影响外面的变量值

[root@repo-client scripts]# cat test1
#变量赋值
test () {
        NAME=mage
        echo NAME=$NAME
}
[root@repo-client scripts]# . test1     #.或source是在当前shell中执行
[root@repo-client scripts]# test    #函数内的变量赋值
NAME=mage
[root@repo-client scripts]# NAME=wang       #重新赋值
[root@repo-client scripts]# echo $NAME      #显示
wang
[root@repo-client scripts]# test            #再次执行test函数
NAME=mage
[root@repo-client scripts]# echo $NAME      #再查看$NAME,是函数内的赋值;因函数内的变量和当前进程是同级关系,在同一个shell中,不是子进程。
mage

范例2:避免同名变量的冲突,local

若函数内定义的变量只在函数内有效,需要定义成本地变量,即使用local定义,只在函数体内或子进程有效。

[root@repo-client scripts]# type local
local is a shell builtin
[root@repo-client scripts]# help local
local: local [option] name[=value] ...
    Define local variables.

    Create a local variable called NAME, and give it VALUE.  OPTION can
    be any option accepted by `declare'.

    Local variables can only be used within a function; they are visible
    only to the function where they are defined and its children.

    Exit Status:
    Returns success unless an invalid option is supplied, an error occurs,
    or the shell is not executing a function.

[root@repo-client scripts]# cat test1
#变量赋值为local
test () {
        local NAME
        NAME=mage       #这两条可以合并为local NAME=mage
        echo NAME=$NAME
}
[root@repo-client scripts]# . test1
[root@repo-client scripts]# test
NAME=mage
[root@repo-client scripts]# NAME=wang
[root@repo-client scripts]# echo $NAME
wang
[root@repo-client scripts]# test            #函数内变量
NAME=mage
[root@repo-client scripts]# echo $NAME      #函数外同名变量,不冲突了
wang

函数的调用方式

  • 可在交互式环境下定义函数

  • 可将函数放在脚本文件中作为它的一部分

  • 可放在只包含函数的单独文件中

调用:函数只有被调用才会执行,通过给定函数名调用函数,函数名出现的地方,会被自动替换为函数代码

函数的生命周期:被调用时创建,返回时终止

#系统自带的函数文件,可以在脚本中直接调用

[root@repo-client scripts]# grep  -E -A10 "^(action|success)" /etc/init.d/functions
success() {
    [ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_success
    return 0
}
# Log that something failed
failure() {
    local rc=$?
    [ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_failure
    [ -x /bin/plymouth ] && /bin/plymouth --details
    return $rc
}
action() {
    local STRING rc

    STRING=$1
    echo -n "$STRING "
    shift
    "$@" && success $"$STRING" || failure $"$STRING"
    rc=$?
    echo
    return $rc
}
#action函数里包含了success和failure两个函数,直接调用时,会显示成功和shi'bai
[root@repo-client scripts]# . /etc/init.d/functions
[root@repo-client scripts]# action "rm -rf /*"
rm -rf /*                                                  [  OK  ]
[root@repo-client scripts]# action ldsfe flejlf
ldsfe -bash: flejlf: command not found                     [FAILED]

2.3 函数返回值

1、函数的执行结果返回值:

  • 使用echo等命令进行输出

  • 函数体中调用命令的输出结果

2、函数的退出状态码:

默认取决于函数中执行的最后一条命令的退出状态码

3、自定义退出状态码,其格式为:

  • return 从函数中返回,用最后状态命令决定返回值

  • return 0 无错误返回

  • return 1-255 有错误返回
#1)使用exit退出,返回值100
[root@repo-client scripts]# cat test1
#5)变量赋值
test () {
        local NAME
        NAME=mage
        echo NAME=$NAME
        exit 100
}
[root@repo-client scripts]# cat test1.sh
#!/bin/bash
#调用test1函数
. test1
test
#返回值
echo statue=$?
[root@repo-client scripts]# bash test1.sh
NAME=mage           #只显示NAME,而无statue值,因为调用test1函数后,exit就退出整个脚本了,不会执行echo statue值

#2)使用return退出,返回值也是100
[root@repo-client scripts]# cat test1
#5)变量赋值
test () {
        local NAME
        NAME=mage
        echo NAME=$NAME
        #exit 100
        return 100
}
[root@centos7 scripts]# bash test1.sh       #执行后,只退出函数,还执行echo statue
NAME=mage
statue=100

2.4 交互式转化批处理工具 expect

expect 是由Don Libes基于 Tcl( Tool Command Language )语言开发的,主要应用于自动化交互式操作的场景,借助 expect 处理交互的命令,可以将交互过程如:ssh登录,ftp登录等写在一个脚本上,使之自动化完成。尤其适用于需要对多台服务器执行相同操作的环境中,可以大大提高系统管理人员的工作效率。最小化安装系统中没有这个命令,yum安装即可。

expect 语法:

expect [选项] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]

常见选项:

-c:从命令行执行expect脚本,默认expect是交互地执行的
-d:可以输出调试信息

示例:

expect -c 'expect "\n" {send "pressed enter\n"}'
expect -d ssh.exp

[root@centos7 yum.repos.d]# expect -c 'expect "\n" {send "pressed enter\n"}'

pressed enter

expect中相关命令

  • spawn 启动新的进程
  • expect 从进程接收字符串
  • send 用于向进程发送字符串
  • interact 允许用户交互

  • exp_continue 匹配多个字符串在执行动作后加此命令

expect最常用的语法(tcl语言:模式-动作)

单一分支模式语法:匹配到hi后,会输出“you said hi”,并换行

#捕获hi字符,出现后,打印you said hi
[root@centos7 expect]# expect
expect1.1> expect "hi" {send "You said hi\n"}
halefhiald
You said hi
expect1.2> exit

多分支模式语法:匹配hi,hello,bye任意字符串时,执行相应输出。不过是一次性的,等同如下

[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> exit

[root@centos7 expect]# 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:从10.0.0.8主机,非交互式复制fstab文件到7主机的data目录下

[root@centos7 expect]# cat expect1
#!/usr/bin/expect
spawn scp /etc/fstab 192.168.100.11:/data
expect {
    "yes/no" { send "yes\n";exp_continue }
    "password" { send "dongdong\n" }
}
expect eof
[root@centos7 expect]# chmod +x expect1
[root@centos7 expect]# ./expect1
spawn scp /etc/fstab 192.168.100.11:/data
The authenticity of host '192.168.100.11 (192.168.100.11)' can't be established.
ECDSA key fingerprint is SHA256:LsADkBrAATQSCqxKP9lZXDYm2WncbAvsH3M1Z0ubNpE.
ECDSA key fingerprint is MD5:5c:bf:b4:5d:6a:24:38:4e:1c:1e:47:d0:b9:92:c2:08.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.100.11' (ECDSA) to the list of known hosts.
root@192.168.100.11's password:
fstab                                                                                                       100%  596   126.4KB/s   00:00

[root@c7-test ~]# ll /data/fstab
-rw-r--r-- 1 root root 596 Mar 25 21:52 /data/fstab

范例2:自动登录

[root@centos7 expect]# cat expect2
#!/usr/bin/expect
spawn ssh 192.168.100.11
expect {
    "yes/no" { send "yes\n";exp_continue }
    "password" { send "dongdong\n" }
}
interact

[root@centos7 expect]# chmod +x expect2
[root@centos7 expect]# ./expect2
spawn ssh 192.168.100.11
root@192.168.100.11's password:
Last login: Thu Mar 25 21:53:35 2021 from 192.168.100.1
[root@c7-test ~]# exit
logout
Connection to 192.168.100.11 closed.
[root@centos7 expect]#

范例3:定义expect 变量实现非交互式登录

[root@centos7 expect]# cat expect3
#!/usr/bin/expect
set ip 192.168.100.11
set user root
set password dongdong
set timeout 10
spawn ssh $user@$ip
expect {
    "yes/no" { send "yes\n";exp_continue }
    "password" { send "$password\n" }
}
interact

#测试,删除测试机上的.ssh文件后,执行
[root@c7-test ~]# rm /root/.ssh -rf
[root@centos7 expect]# chmod +x expect3
[root@centos7 expect]# ./expect3
spawn ssh root@192.168.100.11
root@192.168.100.11's password:
Last login: Thu Mar 25 21:56:36 2021 from 192.168.100.12
[root@c7-test ~]# exit
logout
Connection to 192.168.100.11 closed.
[root@centos7 expect]#

范例4:expect 位置参数,相当于$1 $2等

[root@centos7 expect]# 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@c7-test ~]# rm /root/.ssh -rf
[root@centos7 expect]# chmod +x expect4
#命令后加位置参数
[root@centos7 expect]# ./expect4 192.168.100.11 root dongdong
spawn ssh root@192.168.100.11
root@192.168.100.11's password:
Last login: Thu Mar 25 22:08:21 2021 from 192.168.100.12
[root@c7-test ~]# exit
logout
Connection to 192.168.100.11 closed.
[root@centos7 expect]#

范例5:expect 非交互登录到对方主机,执行多个命令

[root@centos7 expect]# cat expect5
#!/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" }
}
#创建用户haha,并设置密码
expect "]#" { send "useradd haha\n" }
expect "]#" { send "echo $password |passwd --stdin haha\n" }
send "exit\n"
expect eof

[root@centos7 expect]# chmod +x expect5
[root@centos7 expect]# ./expect5 192.168.100.11 root dongdong
spawn ssh root@192.168.100.11
root@192.168.100.11's password:
Last login: Thu Mar 25 22:11:25 2021 from 192.168.100.12
[root@c7-test ~]# useradd haha
[root@c7-test ~]# echo dongdong |passwd --stdin haha
Changing password for user haha.
passwd: all authentication tokens updated successfully.
[root@c7-test ~]# exit
logout
Connection to 192.168.100.11 closed.
[root@centos7 expect]#

[root@c7-test ~]# getent passwd|grep haha
haha:x:2011:2011::/home/haha:/bin/bash

范例6:shell脚本调用expect,登录到对方主机,并创建用户

[root@centos7 expect]# cat expect6.sh
#!/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 $passwordu |passwd --stdin hehe\n" }
expect "]#" { send "exit\n" }
expect eof
EOF
[root@centos7 expect]# chmod +x expect6.sh
[root@centos7 expect]# ./expect6.sh 192.168.100.11 root dongdong
spawn ssh root@192.168.100.11
root@192.168.100.11's password:
Last login: Thu Mar 25 22:19:43 2021 from 192.168.100.12
[root@c7-test ~]# useradd hehe
[root@c7-test ~]# echo dongdong |passwd --stdin hehe
Changing password for user hehe.
passwd: all authentication tokens updated successfully.
[root@c7-test ~]# exit
logout
Connection to 192.168.100.11 closed.
[root@centos7 expect]#
[root@c7-test ~]# getent passwd|grep hehe
hehe:x:2012:2012::/home/hehe:/bin/bash

3 数组array

3.1 数组介绍

变量:存储单个元素的内存空间

数组:存储多个元素的连续的内存空间,相当于多个变量的集合

数组名和索引

  • 索引的编号从0开始,属于数值索引
  • 索引可支持使用自定义的格式,而不仅是数值格式,即为关联索引,bash4.0版本之后开始支持(centos6之后)
  • bash的数组支持稀疏格式(索引不连续)

3.2 声明数组

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

注意:两者不可相互转换

范例:数组不能相互转换

#关联associative数组和索引index数组不能相互转换
[root@centos7 scripts]# declare -A course
[root@centos7 scripts]# declare -a course
-bash: declare: course: cannot convert associative to indexed array

[root@centos7 scripts]# file=(s*.sh)    #定义的是索引数组,不能转换成关联数组
[root@centos7 scripts]# declare -a |grep file
declare -a file='([0]="shift_doit.sh" [1]="shift_useradd.sh")'
[root@centos7 scripts]# declare -A file
-bash: declare: file: cannot convert indexed to associative array

3.3 数组赋值

数组元素的赋值

(1) 一次只赋值一个元素

ARRAY_NAME[INDEX]=VALUE

范例:

weekdays[0]="Sunday"
weekdays[4]="Thursday"

范例:单个元素赋值

[root@centos7 ~]# title[0]=ceo
[root@centos7 ~]# title[1]=cto
[root@centos7 ~]# echo ${title[0]}
ceo
[root@centos7 ~]# echo ${title[1]}
cto
[root@centos7 ~]# echo ${title}     #不加索引号,就默认第一个
ceo
[root@centos7 ~]# declare -a |grep title
declare -a title='([0]="ceo" [1]="cto")'

(2) 一次赋值全部元素

ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)

范例:

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

范例:一次赋值全部元素

[root@centos7 ~]# name=(mage wang li zhao)
[root@centos7 ~]# declare -a name
[root@centos7 ~]# declare -a |grep name
declare -a name='([0]="mage" [1]="wang" [2]="li" [3]="zhao")'
[root@centos7 ~]# echo ${name[2]}
li
[root@centos7 ~]# echo ${name[*]}
mage wang li zhao

#数值
[root@centos7 scripts]# num=({1..10})
[root@centos7 scripts]# declare -a |grep num
declare -a num='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9" [9]="10")'
[root@centos7 scripts]# echo ${num[6]}
7

#目录中的文件为元素赋值
[root@centos7 scripts]# ls
argsnum.sh     dir.sh         expect    for_99.sh      for_continue.sh  for_prename.sh   guess_number.sh  menu.sh   shift_doit.sh     trap.sh
color.sh       disk_check.sh  file.sh   for_break2.sh  for_mkdir.sh     for_scanhost.sh  hostping.sh      per.sh    shift_useradd.sh
createuser.sh  excute.sh      file.txt  for_break.sh   for_movedir.sh   for_sum.sh       init.sh          rm_mv.sh  trap_exit.sh
[root@centos7 scripts]# file=(f*.sh)        #把f开头的sh文件当做元素赋值
[root@centos7 scripts]# declare -a |grep file
declare -a file='([0]="file.sh" [1]="for_99.sh" [2]="for_break2.sh" [3]="for_break.sh" [4]="for_continue.sh" [5]="for_mkdir.sh" [6]="for_movedir.sh" [7]="for_prename.sh" [8]="for_scanhost.sh" [9]="for_sum.sh")'
[root@centos7 scripts]# echo ${file[5]}
for_mkdir.sh

(3) 只赋值特定元素

ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)

(4) 交互式数组值对赋值

read -a ARRAY

范例:

[root@centos7 scripts]# read -a menu
lamian paomo huimian luzhu douzhi
[root@centos7 scripts]# echo ${menu[3]}
luzhu

3.4 显示所有数组

显示所有数组:

declare -a

范例:

[root@centos7 ~]# declare -a
declare -a BASH_ARGC='()'
declare -a BASH_ARGV='()'
declare -a BASH_LINENO='()'
declare -a BASH_SOURCE='()'
declare -ar BASH_VERSINFO='([0]="4" [1]="2" [2]="46" [3]="2" [4]="release" [5]="x86_64-redhat-linux-gnu")'
declare -a DIRSTACK='()'
declare -a FUNCNAME='()'
declare -a GROUPS='()'
declare -a PIPESTATUS='([0]="0")'

3.5 引用数组

引用数组元素

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

范例:

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

[root@centos7 ~]#

引用数组所有元素

${ARRAY_NAME[*]}
${ARRAY_NAME[@]}

范例:

[root@centos7 ~]# echo ${title[@]}
ceo coo cto
[root@centos7 ~]# echo ${title[*]}
ceo coo cto

数组的长度,即数组中元素的个数

${#ARRAY_NAME[*]}
${#ARRAY_NAME[@]}

范例:

[root@centos7 ~]# echo ${#title[*]}
3
[root@centos7 ~]# alpha=({a..z})
[root@centos7 ~]# echo ${#alpha[@]}
26

3.6 删除数组

删除数组中的某元素,会导致稀疏格式,数组不连续

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

[root@centos7 ~]# unset alpha[3]
[root@centos7 ~]# echo ${#alpha[@]}
25
[root@centos7 ~]# echo ${alpha[2]}
c
[root@centos7 ~]# echo ${alpha[4]}
e
[root@centos7 ~]# echo ${alpha[3]}

[root@centos7 ~]# echo ${alpha[*]}
a b c e f g h i j k l m n o p q r s t u v w x y z

删除整个数组

unset ARRAY

范例:

[root@centos7 ~]# unset title
[root@centos7 ~]# echo ${title[*]}

[root@centos7 ~]# echo ${#title[*]}
0

3.7 关联数组

declare -A ARRAY_NAME
ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2'...)

注意:关联数组必须先声明再调用,下标随意定义。一个数组中可以存放多个变量(key)和值(value)。

范例:未定义关联数组

#如果默认创建关联数组,会当成普通索引数组,下标不合理,会合并成一个下标,即0
[root@centos7 ~]# magedu[ceo]=mage
[root@centos7 ~]# declare -a |grep magedu   #第一次定义的下标是ceo,但识别出来的只是0
declare -a magedu='([0]="mage")'
[root@centos7 ~]# magedu[cto]=wang      #第二次定义的下标是cto,也会当做0,所以替换之前的赋值
[root@centos7 ~]# echo ${magedu[ceo]}   
wang
[root@centos7 ~]# declare -a |grep magedu   #只保留最后一次的赋值
declare -a magedu='([0]="wang")'

范例:定义关联数组

[root@centos7 ~]# declare -A magedu
[root@centos7 ~]# magedu[ceo]=mage
[root@centos7 ~]# magedu[cto]=wang
[root@centos7 ~]# echo ${magedu[ceo]}
mage
[root@centos7 ~]# echo ${magedu[cto]}
wang
[root@centos7 ~]# declare -a |grep magedu
[root@centos7 ~]# declare -A |grep magedu
declare -A magedu='([ceo]="mage" [cto]="wang" )'
[root@centos7 ~]#

范例:定义一个数组多个元素

[root@centos7 ~]# declare -A student
[root@centos7 ~]# student[name1]=lijun
[root@centos7 ~]# student[name2]=ziqing
[root@centos7 ~]# student[age1]=18
[root@centos7 ~]# student[age2]=20
[root@centos7 ~]# student[age2]=16
[root@centos7 ~]# declare -A |grep student
declare -A student='([age2]="16" [age1]="18" [name2]="ziqing" [name1]="lijun" )'
[root@centos7 ~]# student[gender1]=m
[root@centos7 ~]# student[gender2]=f
[root@centos7 ~]# student[city1]=nanjing
[root@centos7 ~]# student[city2]=beijing

[root@centos7 ~]# for i in {1..10};do echo student[name$i]=${student[name$i]}; done
student[name1]=lijun
student[name2]=ziqing
student[name3]=
student[name4]=
student[name5]=
student[name6]=
student[name7]=
student[name8]=
student[name9]=
student[name10]=
[root@centos7 ~]#

3.8 范例

范例1:生成10个随机数保存于数组中,并找出其最大值和最小值

[root@centos7 scripts]# cat min_max.sh
#!/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

[root@centos7 scripts]# bash min_max.sh
All numbers are 19010 26392 30183 6597 25443 9142 1734 14084 1773 19250
The max numbers is 30183
The min numbers is 1734
[root@centos7 scripts]# bash min_max.sh
All numbers are 29778 25987 3225 6644 4313 14984 14970 9396 19655 18413
The max numbers is 29778
The min numbers is 3225

范例2:输入若干个数值存入数组中,采用冒泡算法进行升序或降序排序

[root@centos7 scripts]# bash maopao.sh
#!/bin/bash
echo "请输入任意数量的数值,空格隔开:"
read -a array
    for((i=0;i<${#array[*]}-1;i++))
    do
        for((j=0;j<${#array[*]}-i-1;j++))
        do
            if [ ${array[j]} -gt ${array[j+1]} ];then
                temp=${array[j]}
                array[j]=${array[j+1]}
                array[j+1]=$temp
             fi
        done
        echo "第`expr $i + 1`次排序结果: ${array[*]}"
done
echo "排序结果为:${array[*]}"

[root@centos7 scripts]# bash maopao.sh
请输入任意数量的数值,空格隔开:
1 5 3 8 0 6
第1次排序结果: 1 3 5 0 6 8
第2次排序结果: 1 3 0 5 6 8
第3次排序结果: 1 0 3 5 6 8
第4次排序结果: 0 1 3 5 6 8
第5次排序结果: 0 1 3 5 6 8
排序结果为:0 1 3 5 6 8
[root@centos7 scripts]# bash maopao.sh
请输入任意数量的数值,空格隔开:
56 129 2 5 -3 12 0 35
第1次排序结果: 56 2 5 -3 12 0 35 129
第2次排序结果: 2 5 -3 12 0 35 56 129
第3次排序结果: 2 -3 5 0 12 35 56 129
第4次排序结果: -3 2 0 5 12 35 56 129
第5次排序结果: -3 0 2 5 12 35 56 129
第6次排序结果: -3 0 2 5 12 35 56 129
第7次排序结果: -3 0 2 5 12 35 56 129
排序结果为:-3 0 2 5 12 35 56 129
上一篇:loadrunner Controller 删除available scripts中无用脚本


下一篇:Cobalt strike深度学习笔记