Detailed explanation of the usage of the function array of the Ubuntu system network configuration and shell script programming

1. Summary of Ubuntu system network configuration

1.1 Host name

Modify hostname

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 NIC name

The default ubuntu network card name is similar to CentOS 7, such as: ens33, ens38, etc.

Modify the network card name to the traditional naming method:

#查看网卡默认名称为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 network card configuration

Official website document:
Help document: https://help.ubuntu.com/
https://help.ubuntu.com/lts/serverguide/network-configuration.html.zh-CN
Network configuration document: https://ubuntu.com /server/docs/network-configuration

Network configuration file: yaml format
1) -List : indicates multiple members or []
2) Key (variable name): value
3) Different levels are indented and unified

1.3.1 Configure to obtain IP automatically

example:

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 Configure static IP

example:

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

View ip and 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

View routing table 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

View 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 Configure multi-card static IP and static routing

1. Multiple network card configurations are in one file

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. Each network card has its own configuration file

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 introduction

  • Function function is a statement block composed of several shell commands to realize code reuse and modular programming
  • It is similar in form to the shell program, the difference is that it is not a separate process and cannot run independently, but a part of the shell program

Functions and shell programs are similar, the difference is

==The Shell program runs in the sub Shell, and the Shell function runs in the current Shell ==. Therefore, in the current Shell, the function can modify the variables in the shell

2.1 Management functions

Function consists of two parts: function name and function body

Help see: help function

2.1.1 Define function

format:

#语法一:
func_name (){
...函数体...
}
#语法二:
function func_name {
...函数体...
}
#语法三:
function func_name () {
...函数体...
}

Example of function definition: function definition and call in one script

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

Example: Separate function definition and call

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

Example: Install functions of commonly used software

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

Example: Simple operation and maintenance menu script for calling functions

[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 View function

#查看当前已定义的函数名
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 Delete function

unset func_name     #删除函数

2.2 Function call

Example 1: The variable assignment performed in the function body affects the value of the variable outside

[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

Example 2: Avoid conflicts of variables with the same name, local

If the variable defined in the function is only valid in the function, you need to define the local variable, that is, use the local definition, which is only valid in the function body or subprocess.

[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

How to call the function

  • Functions can be defined in an interactive environment

  • Functions can be placed in script files as part of it

  • Can be placed in a separate file containing only functions

Calling: The function will only be executed when it is called. The function is called by the given function name, and the function code will be automatically replaced wherever the function name appears.

The life cycle of the function: created when it is called, and terminated when it returns

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

[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 Function return value

1. The return value of the execution result of the function:

  • Use commands such as echo for output

  • The output result of the calling command in the function body

2. The exit status code of the function:

The default depends on the exit status code of the last command executed in the function

3. Custom exit status code, its format is:

  • return Return from the function, use the final status command to determine the return value

  • return 0 return without error

  • return 1-255 return with error
#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 Interactive conversion batch tool expect

Expect is developed by Don Libes based on the Tcl (Tool Command Language) language. It is mainly used in automated interactive operation scenarios. With the help of expect to process interactive commands, interactive processes such as ssh login, ftp login, etc. can be written on a script , Make it complete automatically. It is especially suitable for environments where the same operation needs to be performed on multiple servers, which can greatly improve the work efficiency of system administrators. There is no such command in the minimal installation system, just install it with yum.

expect syntax:

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

Common options:

-c: execute the expect script from the command line, the default expect is executed interactively
-d: debug information can be output

Example:

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

Related commands in expect

  • spawn starts a new process
  • expect to receive a string from the process
  • send is used to send a string to the process
  • interact allows user interaction

  • exp_continue matches multiple strings, add this command after performing an action

The most commonly used grammar of expect (tcl language: mode-action)

Single branch pattern syntax: after matching hi, it will output "you said hi" and wrap

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

Multi-branch mode syntax: when matching any string of hi, hello, bye, execute the corresponding output. But it is a one-off, equivalent to the following

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

Example 1: From the 10.0.0.8 host, non-interactively copy the fstab file to the data directory of the 7 host

[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.
[email protected]'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

Example 2: Automatic login

[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
[email protected]'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]#

Example 3: Define expect variable to realize non-interactive login

[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 [email protected]
[email protected]'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]#

Example 4: Expect positional parameters, equivalent to $1 $2, etc.

[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 [email protected]
[email protected]'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]#

Example 5: Expect to log in to the other party's host non-interactively and execute multiple commands

[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 [email protected]
[email protected]'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

Example 6: The shell script calls expect, logs in to the host of the other party, and creates a user

[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 [email protected]
[email protected]'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 Introduction to arrays

Variable: the memory space to store a single element

Array: A continuous memory space that stores multiple elements, which is equivalent to a collection of multiple variables

Array name and index

  • The index number starts from 0 and belongs to the numerical index
  • The index can support the use of a custom format, not just a numeric format, that is, an associated index, which is supported after bash4.0 (after centos6)
  • bash arrays support sparse format (the index is not continuous)

3.2 Declaring an array

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

Note: The two cannot be converted to each other

Example: Arrays cannot be converted to each other

#关联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 Array assignment

Assignment of array elements

(1) Assign only one element at a time

ARRAY_NAME[INDEX]=VALUE

example:

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

Example: Assignment of a single element

[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) Assign all elements at once

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

example:

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

Example: Assign all elements at once

[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) Assign only specific elements

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

(4) Interactive array value assignment

read -a ARRAY

example:

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

3.4 Display all arrays

Show all arrays:

declare -a

example:

[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 Referencing an array

Reference array element

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

example:

[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 ~]#

Reference all elements of the array

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

example:

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

The length of the array, that is, the number of elements in the array

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

example:

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

3.6 Delete array

Deleting an element in the array will result in a sparse format and the array is not continuous

[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

Delete the entire array

unset ARRAY

example:

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

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

3.7 Associative array

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

Note: Associative arrays must be declared before calling, and the subscripts are defined at will. Multiple variables (key) and values ​​(value) can be stored in an array.

Example: Undefined associative array

#如果默认创建关联数组,会当成普通索引数组,下标不合理,会合并成一个下标,即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")'

Example: Defining an associative array

[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 ~]#

Example: Define multiple elements in an array

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

Example 1: Generate 10 random numbers and store them in an array, and find the maximum and minimum values

[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

Example 2: Input a number of values ​​and store them in an array, and use the bubble algorithm to sort in ascending or descending order

[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

Guess you like

Origin blog.51cto.com/puppydong/2675635