前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Shell脚本编程_shell简单脚本

Shell脚本编程_shell简单脚本

作者头像
全栈程序员站长
发布2022-11-09 14:39:45
7.5K0
发布2022-11-09 14:39:45
举报

大家好,又见面了,我是你们的朋友全栈君。

文章目录


1. shell 概述

1.1 shell 和 shell 脚本

  shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务,在用户和内核之间充当翻译官的角色,是一个命令解释器。Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 shell。

在这里插入图片描述
在这里插入图片描述

  shell 脚本(shell script),是一种为 shell 编写的脚本程序。业界所说的 shell 通常都是指 shell 脚本,shell 和 shell script 是两个不同的概念。

1.2 Shell 脚本应用场景

  • 重复性操作
  • 交互性任务
  • 批量事务处理
  • 服务运行状态监控
  • 定时任务执行 …

1.3 Linux 中的 shell 类型

以 CentOS 7 为例

代码语言:javascript
复制
[root@c7-1 ~]#cat /etc/shells
/bin/sh			#/bin/sh 是 bash 命令的软链接(已经被 /bin/bash 所替换)
/bin/bash		#基准于 GNU 的框架下发展出的 shell
/usr/bin/sh		#己经被 bash 所替换
/usr/bin/bash	#centos 和 redhat 系统默认使用 bash shell
/bin/tcsh		#csh 的增强版,与 csh 完全兼容,提供更多的功能
/bin/csh		#已经被 /bin/bash 所替换(整合 c shell,提供更多的功能)
/sbin/nologin	#奇怪的 shel1,这个 shell 可以让用户无法登录主机

1.4 脚本实例

代码语言:javascript
复制
[root@c7-1 ~]#curl -s http://47.117.130.238/hello.sh
# ------------------------------------------
# Filename: hello.sh
# Version: 1.0
# Date: 2021/06/06
# Author: gongboyi
# Email: 1520509800@qq.com
# Website: https://www.cnblogs.com/shenyuanhaojie/
# Description: This is the first script
# Copyright: 2021 zhou
# License: GPL
# ------------------------------------------
echo "hello world!"

[root@c7-1 ~]#curl -s http://47.117.130.238/hello.sh|bash
hello world!

2. shell 脚本编程基础

2.1 脚本调试

只检测脚本中的语法错误,无法检查出命令错误,不真正执行脚本

代码语言:javascript
复制
bash -n /path/to/script.sh

调试并执行

代码语言:javascript
复制
bash -x /path/to/script.sh

脚本错误常见的有三种:

代码语言:javascript
复制
语法错误:会导致后续的命令不继续执行,可以用 bash -n 检查错误,提示的出错行数不一定是准确的。
命令错误:后续的命令还会继续执行,用 bash -n 无法检查出来 ,可以使用 bash -x 进行观察。
逻辑错误:只能使用 bash -x 进行观察。

2.2 脚本执行方法

代码语言:javascript
复制
相对路径执行( ./script.sh )在脚本当前目录,脚本需要执行权限
绝对路径执行( /PATH/to/script.sh )无需在脚本目录,脚本需要执行权限
bash 执行( bash /PATH/to/script.sh )bash 后可跟绝对路径和相对路径,脚本无需执行权限
source 执行( source /PATH/to/script.sh )source 后可跟绝对路径和相对路径,脚本无需执行权限
bash < script.sh 或 cat script.sh | bash

示例:

代码语言:javascript
复制
[root@aliyunhost01html]#./hello.sh 
hello world!
[root@aliyunhost01html]#/var/www/html/1.sh 
hello world
[root@aliyunhost01html]#bash /var/www/html/1.sh
hello world
[root@aliyunhost01html]#source /var/www/html/1.sh
hello world
[root@aliyunhost01html]#bash < /var/www/html/1.sh
hello world
[root@aliyunhost01html]#cat /var/www/html/1.sh | bash
hello world

2.3 管道符号 |

将左侧命令的输出结果作为右侧命令的处理对象。 示例:

代码语言:javascript
复制
[root@c7-1 ~]#grep "/sbin/nologin" /etc/passwd | awk -F: '{print $1,$7}'
bin /sbin/nologin
daemon /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
mail /sbin/nologin
......

2.4 交互式硬件设备

代码语言:javascript
复制
标准输入:从该设备接收用户输入的数据
标准输出:通过该设备向用户输出数据
标准错误:通过该设备报告执行出错信息

类型

设备文件

文件描述编号

默认设备

标准输入

/dev/stdin

0

键盘

标准输出

/dev/stdout

1

显示器

标准错误输出

/dev/stderr

2

显示器

代码语言:javascript
复制
[root@c7-1 ~]#ll /dev/std*
lrwxrwxrwx 1 root root 15 9月   2 10:02 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 9月   2 10:02 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 9月   2 10:02 /dev/stdout -> /proc/self/fd/1

2.5 重定向操作

类型

操作符

用途

重定向输入

<

从指定的文件读取数据,而不是从键盘输入

重定向输出

>

将输出结果保存到指定的文件(覆盖原有内容)

>>

将输出结果追加到指定的文件尾部

标准错误输出

2>

将错误信息保存到指定的文件(覆盖原有内容)

2>>

将错误信息追加到指定的文件中

混合输出

&>

将标准输出、标准错误的输出保存到同一个文件中

示例:

代码语言:javascript
复制
#将 ./test.sh 的输出重定向到 log.txt 文件中,同时将标准错误也重定向到 log.txt 文件中
./test.sh > log.txt 2>&1

# nohup ... & 代表后台运行并且生成 nohup.log 日志文件
# command>/dev/null 代表命令输出结果导入到空设备
# 2>&1 代表将标准错误的内容重定向到标准输出,即将程序运行中的错误信息也打印出来
nohup ./test.sh>/dev/null 2>&1 &

参考:重定向理解

2.6 变量

(1) 变量的赋值与引用

① 变量赋值
代码语言:javascript
复制
name='value'

value 可以是以下多种形式:

代码语言:javascript
复制
直接字串:name='root'
变量引用:name="$USER"
命令引用:name=`COMMAND` 或者 name=$(COMMAND)

变量赋值是临时生效,当退出终端后,变量会自动删除,无法持久保存,脚本中的变量随着脚本结束,也会自动删除。

使用 read 从标准输入读取数值:

代码语言:javascript
复制
read -p "提示信息" [变量名]
read -p "提示信息" [变量名] < [文件]

常用选项:

代码语言:javascript
复制
-p   #指定要显示的提示信息
-s   #静默输入,一般用于密码
-n N #指定输入的字符长度 N
-d '字符'	#输入结束符
-t N 	    #TIMEOUT 为 N 秒

示例:

代码语言:javascript
复制
[root@c7-1 ~]#read -p "请输入密码:" passwd
请输入密码:120604
[root@c7-1 ~]#echo $passwd
120604
[root@c7-1 ~]#vim IP.txt
[root@c7-1 ~]#read -p "请输入IP地址:" IP < IP.txt
[root@c7-1 ~]#echo $IP
47.117.130.238
[root@c7-1 ~]#cat IP.txt 
47.117.130.238
#实现运维工作菜单
echo -en "\E[$[RANDOM%7+31];1m"
cat <<EOF 请选择: 1)备份数据库 2)清理日志 3)软件升级 4)软件回滚 5)删库跑路 EOF
echo -en '\E[0m'
read -p  "请输入上面数字1-5: " MENU
[ $MENU -eq 1 ] && ./backup.sh
[ $MENU -eq 2 ] && echo "清理日志"
[ $MENU -eq 3 ] && echo "软件升级"
[ $MENU -eq 4 ] && echo "软件回滚"
[ $MENU -eq 5 ] && echo "删库跑路"
② 变量引用
代码语言:javascript
复制
" "    弱引用,允许通过 $ 符号引用其他变量值
' '    强引用,禁止引用其他变量值,$ 视为普通字符
` `    命令引用,提取命令执行后的输出结果

示例:

代码语言:javascript
复制
[root@c7-1 ~]#username=zc
[root@c7-1 ~]#echo "you are $username"
you are zc
[root@c7-1 ~]#echo 'you are $username'
you are $username
[root@c7-1 ~]#echo `whoami`
root

(2) 变量类型

  • 自定义变量:由用户自己定义、修改、使用,只在用户自己的 shell 环境中有效,因此又称为本地变量
  • 特殊变量:环境变量、只读变量、位置变量、预定义变量
① 自定义变量

基本格式:

代码语言:javascript
复制
变量名=变量值

示例:

代码语言:javascript
复制
[root@c7-1 ~]#username=zc
[root@c7-1 ~]#echo $username
zc
[root@c7-1 ~]#set | grep username #set 显示用户自定义的变量
username=zc
local remote_opts="--username= --config-dir= --no-auth-cache";
--no-auth-cache --username=
[root@c7-1 ~]#unset username
[root@c7-1 ~]#set | grep username
_=username
local remote_opts="--username= --config-dir= --no-auth-cache";
--no-auth-cache --username=
② 环境变量

环境变量分为:

  • 全局变量:不仅对 shell 可见,对其子进程也可见
  • 局部变量:只能在定义其的进程中可见

显示所有环境变量:

代码语言:javascript
复制
env
printenv
export
declare -x

Bash 内建的环境变量:

代码语言:javascript
复制
PATH
SHELL
USER
UID
HOME
PWD
SHLVL	#shell 的嵌套层数
LANG
MAIL
HOSTNAME
HISTSIZE
_ 		#下划线,表示前一命令的最后一个参数

声明全局变量:

代码语言:javascript
复制
export [变量名]
declare -x [变量名]

声明的全局变量虽然在环境变化的时候仍然生效,但是当系统重启后不会保存,要想永久生效可以保存到配置文件中。

变量配置文件:

代码语言:javascript
复制
系统层面:/etc/profile、/etc/bashrc
用户层面:~/.bash_profile、~/.bashrc、~/.bash_history、~/.bash_logout
#系统层面的配置文件通常在登录时加载,用户层面的配置文件只对单个用户生效

参考:Linux环境变量配置全攻略

③ 只读变量

只读变量的声明:

代码语言:javascript
复制
readonly [变量名]
declare -r [变量名]

查看:

代码语言:javascript
复制
readonly [-p]
declare -r

只读变量无法删除或更改,当退出登录或者重启系统会失效。

示例:

代码语言:javascript
复制
[root@c7-1 ~]#readonly PI=3.14
[root@c7-1 ~]#echo $PI
3.14
[root@c7-1 ~]#PI=3.14159
-bash: PI: 只读变量
[root@c7-1 ~]#unset PI
-bash: unset: PI: 无法反设定: 只读 variable
[root@c7-1 ~]#logout
Connection closed by foreign host.
Disconnected from remote host(c7-1) at 21:53:33.
Type `help' to learn how to use Xshell prompt.
[c:\~]$ 
Reconnecting in 5 seconds. Press any key to exit local shell.
...
Connecting to 192.168.10.20:22...
Connection established.
To escape to local shell, press Ctrl+Alt+].
Last login: Fri Sep  3 21:33:20 2021 from 192.168.10.1
[root@c7-1 ~]#echo $PI
[root@c7-1 ~]#
④ 位置变量

在 bash shell 中内置的变量,在脚本代码中调用通过命令行传递给脚本的参数。

代码语言:javascript
复制
$1, $2, ... 对应第1个、第2个等参数,shift [n] 换位置
$0 命令本身,包括路径
$* 传递给脚本的所有参数,全部参数合为一个字符串
$@ 传递给脚本的所有参数,每个参数为独立字符串
$# 传递给脚本的参数的个数
注意:$@ $* 只在被双引号包起来的时候才会有差异

清空所有位置变量:

代码语言:javascript
复制
set --

示例:

代码语言:javascript
复制
[root@c7-1 ~]#cat test.sh 
echo $1 $2 $3
echo $0
echo $?
echo $$
echo $!
echo $#
echo $*
echo $@
[root@c7-1 ~]#bash test.sh a b c
a b c
test.sh
0
72999
3
a b c
a b c
#实现添加用户
[root@c7-1 ~]#cat useradd.sh
#!/bin/bash
useradd $1
echo $2 | passwd --stdin $1
[root@c7-1 ~]#bash useradd.sh zc 123456
更改用户 zc 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@c7-1 ~]#cat /etc/passwd|grep zc
zc:x:1001:1001::/home/zc:/bin/bash
#删库跑路之命令 rm 的安全实现
WARNING_COLOR="echo -e \E[1;31m"
END="\E[0m"
DIR=/tmp/`date +%F_%H-%M-%S`
mkdir $DIR
mv  $*  $DIR
${WARNING_COLOR}Move $* to $DIR $END
⑤ 预定义变量

  预定义变量是在 Shell 一开始时就定义的变量,这一点和默认环境变量有些类似。不同的是,预定义变量不能重新定义,用户只能根据 Shell 的定义来使用这些变量。严格来说,位置参数变量也是预定义变量的一种,只是位置参数变量的作用比较统一,所以我们把位置参数变量单独划分为一类数量。

代码语言:javascript
复制
$? 表示前一条命令执行后的返回状态,返回值为О表示执行正确,返回任何非О值均表示执行出现异常
$$ 表示返回当前进程的进程号
$! 返回最后一个后台进程的进程号

用户可以在脚本中使用以下命令自定义退出状态码:

代码语言:javascript
复制
exit [n]
#脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
#如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码

示例:

代码语言:javascript
复制
#测试网络通信
[root@c7-1 ~]#ping -c1 -W1 47.117.130.238 &> /dev/null
[root@c7-1 ~]#echo $?
0
#测试服务端口
[root@aliyunhost01~]#curl 127.0.0.1:80 &> /dev/null
[root@aliyunhost01~]#echo $?
0

(3) 变量的查看与删除

① 查看已定义的所有变量
代码语言:javascript
复制
set
② 删除变量
代码语言:javascript
复制
unset [变量名]
#只读变量无法删除

(4) 变量的赋值运算

代码语言:javascript
复制
+	加
-	减
\*	乘
/	除
% 	取模,即取余数,示例:9%4=1,5%3=2
** 	乘方

示例:

代码语言:javascript
复制
[root@c7-1 ~]#expr 2 + 3
5
[root@c7-1 ~]#num1=2
[root@c7-1 ~]#num2=3
[root@c7-1 ~]#expr $num1 + $num2
5
[root@c7-1 ~]#expr $num2 / $num1
1
[root@c7-1 ~]#expr $num1 \* $num2
6
[root@c7-1 ~]#expr $num2 % $num1
1
[root@c7-1 ~]#sum=$(($num1**$num2))
[root@c7-1 ~]#echo $sum
8

2.7 算数运算

(1) 实现算数运算的方式

代码语言:javascript
复制
let var=算术表达式
((var=算术表达式))
var=$[算术表达式]
var=$((算术表达式))
var=$(expr arg1 arg2 arg3 ...)
declare -i var = 数值
echo '算术表达式' | bc 

(2) 增强型赋值

代码语言:javascript
复制
+=	# i+=j 相当于 i=i+j
-= 	# i-=j 相当于 i=i-j
*=	# i*=j 相当于 i=i*j 
/=  # i/=j 相当于 i=i/j
%=	# i%=j 相当于 i=i%j 
++  # i++,++i 相当于 i=i+1
--  # i--,--i 相当于 i=i-1

说明:i++ 和 ++i,i– 和 –i 含义是不同的,我们用具体的示例来解释

代码语言:javascript
复制
# i 先将值赋值给了 j ,然后再完成自加
[root@c7-1 ~]#unset i j; i=1; let j=i++; echo "i=$i,j=$j"
i=2,j=1
# i 完成自加后赋值给 j
[root@c7-1 ~]#unset i j; i=1; let j=++i; echo "i=$i,j=$j"
i=2,j=2

(3) 逻辑运算

代码语言:javascript
复制
布尔型变量bool的取值只有false和true
true	1
false	0

& 与

代码语言:javascript
复制
 1 与 1 = 1
1 与 0 = 0
0 与 1 = 0
0 与 0 = 0

| 或

代码语言:javascript
复制
 1 或 1 = 1
1 或 0 = 1
0 或 1 = 1
0 或 0 = 0 

! 非

代码语言:javascript
复制
! 1 = 0
! 0 = 1

^ 异或

代码语言:javascript
复制
相同为假,不同为真
1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 1 = 1
0 ^ 0 = 0 

&& 短路与

代码语言:javascript
复制
CMD1 短路与 CMD2
第一个CMD1结果为真(1),第二个CMD2必须要参与运算,才能得到最终的结果
第一个CMD1结果为假(0),总的结果必定为0,因此不需要执行CMD2

|| 短路或

代码语言:javascript
复制
CMD1 短路或 CMD2
第一个CMD1结果为真(1),总的结果必定为1,因此不需要执行CMD2
第一个CMD1结果为假(0),第二个CMD2必须要参与运算,才能得到最终的结果

(4) 随机数生成器变量 – $RANDOM

bash 有内建的随机数生成器:$RANDOM(0-32767)

代码语言:javascript
复制
#生成 0-49 之间随机数
echo $[$RANDOM%50]
#更换字体颜色,每次输出的结果显示不同的颜色
echo -e "\E[1;$[RANDOM%7+31]m hello \e[0m"

(5) “空” 设备 – /dev/null

“空” 设备,也被称为黑洞。任何输入到这个设备的数据都将被直接丢弃(但是操作返回成功 $? 值为 0)。最常用的用法是把不需要的输出重定向到这个文件。例如:

代码语言:javascript
复制
#将标准输出和错误输出重定向到 /dev/null,运行这个脚本不会输出任何信息到终端
run.sh 1>/dev/null 2>&1  

(6) “零” 设备 – /dev/zero

“零” 设备,可以无限的提供空字符(0x00,ASCII代码NUL)。常用来生成一个特定大小的文件。例如:

代码语言:javascript
复制
#产生一个 1k 大小的文件 output.txt
dd if=/dev/zero of=./output.txt bs=1024 count=1 

(7) 随机数生成器设备 – /dev/random /dev/urandom

随机数设备,提供不间断的随机字节流。二者的区别是 /dev/random 产生随机数据依赖系统中断,当系统中断不足时,/dev/random 设备会 “挂起”,因而产生数据速度较慢,但随机性好;/dev/urandom 不依赖系统中断,数据产生速度快,但随机性较低。

代码语言:javascript
复制
#生成随机密码
< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-32};echo;
openssl rand -base64 32
date +%s | sha256sum | base64 | head -c 32 ; echo
tr -cd '[:alnum:]' < /dev/urandom | fold -w30 | head -n1
strings /dev/urandom | grep -o '[[:alnum:]]' | head -n 30 | tr -d '\n'; echo
dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 -w 0 | rev | cut -b 2- | rev
date | md5sum

参考:Linux 中的虚拟设备

2.8 echo -n/-e 的用法

echo -n 不换行输出

代码语言:javascript
复制
[root@c7-1 ~]#echo "123456"
123456
[root@c7-1 ~]#echo -n "123456"
123456[root@c7-1 ~]#

echo -e 处理特殊字符

代码语言:javascript
复制
若字符串中出现以下字符,则特别加以处理,而不会将它当成一般文字输出
\a 发出警告声
\b 删除前一个字符
\c 最后不加上换行符号
\f 换行但光标仍旧停留在原来的位置
\n 换行且光标移至行首
\r 光标移至行首,但不换行
\t 插入tab
\v 与\f相同
\\ 插入\字符
\nnn 插入nnn(八进制)所代表的ASCII字符

参考: echo -n/-e 用法 echo 命令详解 echo 显示内容带颜色

2.9 条件测试命令

判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成测试过程。

代码语言:javascript
复制
若真,则状态码变量 $? 返回 0
若假,则状态码变量 $? 返回 1

条件测试格式:

代码语言:javascript
复制
test <选项> <测试的内容>
[ 选项 测试的内容 ]
[[ 选项 测试的内容 ]]

常用测试选项:

代码语言:javascript
复制
-e	测试目录或文件是否存在,[ -a file ] 等于 [ -e file ]
-d	测试是否为目录
-f	测试是否为文件
-r	测试当前用户是否有权限读取
-w	测试当前用户是否有权限写入
-x	测试当前用户是否有权限执行
-u 	测试文件是否存在且拥有 suid 权限
-g	测试文件是否存在且拥有 sgid 权限
-k	测试文件是否存在且拥有 sticky 权限
-z	如果 STRING 的长度为零则为真
-n	如果 STRING 的长度非零则为真
-b	测试文件是否存在并且是块设备文件
-c	测试文件是否存在并且是字符设备文件
-L	测试文件是否存在并且是链接文件
-p	测试文件是否存在并且是管道文件
-S	测试文件是否存在并且是套接字文件
-s	测试文件是否存在并且文件大小为空

其他文件属性测试选项:

代码语言:javascript
复制
-t fd 		fd 文件描述符是否在某终端已经打开
-N FILE 	文件自从上一次被读取之后是否被修改过
-O FILE 	当前有效用户是否为文件属主
-G FILE 	当前有效用户是否为文件属组
FILE1 -ef FILE2		FILE1 是否是 FILE2 的硬链接
FILE1 -nt FILE2 	FILE1 是否新于 FILE2(mtime)
FILE1 -ot FILE2 	FILE1 是否旧于 FILE2

字符串比较:

代码语言:javascript
复制
[ STRING1 = STRING2 ]	两个字符串相等为真
[ STRING1 != STRING2 ]	两个字符串不等为真                    
[ STRING1 < STRING2 ]	如果 STRING1 按字典顺序排在 STRING2 之前,则为真
[ STRING1 > STRING2 ]	如果 STRING1 按字典顺序排在 STRING2 之后,则为真
[[ STRING1 == STRING2 ]]	左侧字符串是否和右侧的 PATTERN 相同
[[ STRING1 =~ STRING2 ]]	左侧字符串是否能够被右侧的正则表达式的 PATTERN 所匹配

逻辑运算与、或、非(条件组合测试):

代码语言:javascript
复制
[ ! EXPR ]	逻辑非,与表达相反的结果为真
[ EXPR1 -a EXPR2 ]	逻辑与,要同时满足多个表达式才为真,等同于 &&
[ EXPR1 -o EXPR2 ]	逻辑或,满足其中任意一个表达式即为真,等同于 ||
#如果 && 和 || 混合使用,&& 要在前,|| 在后

变量测试:

代码语言:javascript
复制
-v VAR	变量 VAR 是否设置
-r VAR	变量 VAR 是否设置并引用

数值比较:

代码语言:javascript
复制
-eq	是否等于
-ne 是否不等于
-gt 是否大于
-ge 是否大于等于
-lt 是否小于
-le 是否小于等于

2.10 流程控制

(1) if 条件语句

格式:

代码语言:javascript
复制
if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else
COMMANDS; ] fi

单分支:

代码语言:javascript
复制
if	判断条件;then
条件为真的分支代码
fi

双分支:

代码语言:javascript
复制
if	判断条件;then
条件为真的分支代码
else
条件为假的分支代码
fi

多分支:

代码语言:javascript
复制
if	判断条件1;then
条件1为真的分支代码
elif	判断条件2;then
条件2为真的分支代码
elif	判断条件3;then
条件3为真的分支代码
...
else
以上条件都为假的分支代码
fi

示例:

代码语言:javascript
复制
###单分支
[root@c7-1 ~]#cat if1.sh 
#!/bin/bash
if ping -c1 -w2 www.baidu.com &> /dev/null;then
echo "网络连通"
fi
[root@c7-1 ~]#bash if1.sh 
网络连通
###双分支
[root@c7-1 ~]#cat if2.sh
#!/bin/bash
IP=47.117.130.238
if ping -c1 -i 0.2 -w2 $IP &> /dev/null;then
echo "主机在线"
else 
echo "主机断联"
fi
[root@c7-1 ~]#bash if2.sh
主机在线
###多分支
[root@c7-1 ~]#cat if3.sh
#!/bin/bash
read -p "请输入分数:" GRADE
if [ $GRADE -le 100 -a $GRADE -ge 80 ];then
echo "成绩优秀"
elif [ $GRADE -lt 80 -a $GRADE -ge 60 ];then
echo "成绩合格"
else 
echo "不及格"
fi
[root@c7-1 ~]#bash if3.sh
请输入分数:65
成绩合格

(2) case 多分支语句

格式:

代码语言:javascript
复制
case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac

分支结构:

代码语言:javascript
复制
case 变量值 in
模式一)
命令序列
;;
模式二)
命令序列
;;
模式三)
命令序列
;;
......
*)
默认命令序列
esac                 

case 支持 glob 风格的通配符:

代码语言:javascript
复制
*	任意长度任意字符
?	任意单个字符
[]	指定范围内的任意单个字符
|	逻辑或

示例:

代码语言:javascript
复制
###判断输入yes或no
[root@c7-1 ~]#cat YESorNO.sh
#!/bin/bash
read -p "请输入YES或NO:" VAR
VAR=`echo $VAR | tr 'A-Z' 'a-z'`
case $VAR in
y|ye|ys|yes)
echo "你输入的是YES"
;;
n|no)
echo "你输入的是NO"
;;
*)
echo "输入错误,请重新输入YES或NO"
esac
[root@c7-1 ~]#bash YESorNO.sh
请输入YES或NO:y
你输入的是YES
###httpd 服务控制
[root@c7-1 ~]#cat httpd.sh 
#!/bin/bash
case $1 in
start)
/usr/bin/systemctl $1 httpd
/usr/bin/ps aux | grep httpd
echo "httpd start"
;;
stop)
/usr/bin/systemctl $1 httpd
/usr/bin/ps aux | grep httpd
echo "httpd stop"
;;
restart)
echo "httpd stop......"
/usr/bin/ps aux | grep httpd
/usr/bin/systemctl $1 httpd
echo "httpd restart......"
/usr/bin/ps aux | grep httpd
;;
status)
/usr/bin/systemctl $1 httpd
;;
*)
echo "请输入 start|stop|restart|status"
esac
[root@c7-1 ~]#./httpd.sh status | grep active
Active: active (running) since 一 2021-09-06 11:20:30 CST; 35s ago

(3) for 循环

格式:

代码语言:javascript
复制
#格式一
for 变量名 in 取值列表;
do
循环体
done
#格式二
for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))
do
循环体
done

示例:

代码语言:javascript
复制
#!/bin/bash
for i in `seq 10`
do 
echo $i
done
————————————————————
#!/bin/bash
#输出 10 次 hello world
a=10
for i in `seq $a`
do
echo "hello world"
done
————————————————————
#!/bin/bash
#输出 0-10,间隔 2
for i in { 
0..10..2}
do
echo $i
done
————————————————————
#!/bin/bash
for ((i=1;i<=5;i++))
do 
echo $i
done
————————————————————
#!/bin/bash
#批量创建用户并修改密码
for i in { 
1..5}
do
useradd user$i
echo "123456" | passwd --stdin user$i
done
————————————————————
#!/bin/bash
#根据用户列表文件批量创建用户并修改密码
ULIST=$(cat /opt/user.txt)
for UNAME in $ULIST
do
useradd $UNAME
echo "123456"|passwd --stdin $UNAME &> /dev/null
done
————————————————————
#!/bin/bash
#根据用户列表文件删除用户
ULIST=$(cat /opt/user.txt)
for UNAME in $ULIST
do
userdel -r $UNAME &> /dev/null
done
————————————————————
#!/bin/bash
#测试文件中的 IP 是否连通
IPLIST=$(cat /opt/ipaddrs.txt)
for IP in $IPLIST
do
ping -c 3 -i 0.2 -W 3 $IP &> /dev/null
if [ $? -eq 0 ];then
echo "Host $IP is up"
else
echo "Host $IP is down"
fi
done
————————————————————
#!/bin/bash
#测试 IP 网段
for i in { 
1..255}
do
ping -c 2 -i 0.1 -W 1 192.168.10.$i &>/dev/dull
if [ $? -eq 0 ]
then
echo "192.168.10.$i is up"
else
echo "192.168.10.$i is down"
fi
done
————————————————————
#!/bin/bash
#密码登录与确认
init=123456
for i in { 
1..3}
do
read -p "请输入免密:" pass
if [ $pass == $init ];then
echo "密码正确"
exit
fi
done
echo "密码错误"
————————————————————
#!/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
————————————————————
#!/bin/bash
#九九乘法表二
for ((i=1;i<10;i++));do
for((j=1;j<=i;j++));do
echo -e "${j}x${i}=$[i*j]\t\c"
done
echo
done
————————————————————
#!/bin/bash
#生成菱形
read -p "请输入菱形行数:" RM
for ((i=1;i<=$RM;i++))
do
for ((j=$RM;j>$i;j--))
do
echo -n " "
done
for ((n=1;n<=$i;n++))
do
echo -n "* "
done
echo ""
done
for ((k=$RM-1;k>=1;k--))
do
for ((m=$RM;m>$k;m--))
do
echo -n " "
done
for ((l=1;l<=$k;l++))
do
echo -n "* "
done
echo ""
done

(4) while 循环

格式:

代码语言:javascript
复制
while CONDITION; 
do
循环体
done

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

示例:

代码语言:javascript
复制
#!/bin/bash
i=1
while [ $i -le 5 -a $i -ge 0 ]
do
echo $i 
let i++
done
echo -e "\033[31m最后的i值为:$i\033[0m"
echo -e "\033[31m循环结束\033[0m"
————————————————————
#!/bin/bash
#httpd 服务控制
while pgrep "httpd" &> /dev/null
do
echo -e "\033[32mhttpd 服务正在运行\033[0m"
sleep 2
done
echo -e "\033[31mhttpd 服务异常\033[0m" 
echo -e "\033[32m安装并启动 httpd 服务......\033[0m"
yum -y install httpd &>/dev/null
systemctl start httpd &>/dev/null && systemctl enable httpd &> /dev/null
systemctl status httpd
————————————————————
#!/bin/bash
#猜数字,猜对了通过 break 退出循环
NUM=8
while true
do
read -p "请输入数字:" SHUZI
if [ $SHUZI -eq $NUM ];then
echo "猜对了"
break
elif [ $SHUZI -gt $NUM ];then
echo "猜大了"
elif [ $SHUZI -lt $NUM ];then
echo "猜小了"
fi
done
————————————————————
#无限循环,break 退出
while true; 
do
循环体
done
while true; 
do
循环体
break
done

while 循环实现磁盘超载邮件告警:

代码语言:javascript
复制
#配置发邮件的邮箱
[root@centos8 ~]#cat .mailrc
set from=1520509800@qq.com
set smtp=smtp.qq.com
set smtp-auth-user=1520509800@qq.com
set smtp-auth-password=esvnhbnqocirbicf
set smtp-auth=login
set ssl-verify=ignore
#告警脚本
[root@centos8 ~]#cat while_diskcheck.sh
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" 1520509800@qq.com
fi
sleep 10
done

(5) until 循环

重复测试某个条件,只要条件不成立则反复执行。

格式:

代码语言:javascript
复制
until CONDITION;
do
循环体
done

说明:

代码语言:javascript
复制
进入条件:CONDITION 为 false
退出条件:CONDITION 为 true

无限循环:

代码语言:javascript
复制
until false;
do
循环体
done

示例:

代码语言:javascript
复制
#!/bin/bash
i=1
sum=0
until [ $i -eq 10 ]
do
sum=$[$i+$sum]
let i++
done
echo $sum 

(6) 循环控制语句 continue

continue [N]:提前结束第 N 层的本轮循环,而直接进入下一轮判断,最内层为第 1 层。

格式:

代码语言:javascript
复制
while CONDITION1; 
do
CMD1
...
if CONDITION2; then
continue
fi
CMDn
...
done

示例:

代码语言:javascript
复制
#!/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

参考:continue-break

(7) 循环控制语句 break

break [N]:提前结束第 N 层整个循环,最内层为第 1 层。

格式:

代码语言:javascript
复制
while CONDITION1; 
do
CMD1
...
if CONDITION2;then
break
fi
CMDn
...
done

示例:

代码语言:javascript
复制
#!/bin/bash
#可测试 break 和 break 2 输出结果有什么不同
#break 退出单个循环,break 2 退出 2 层循环
for ((i=0;i<10;i++));
do
for((j=0;j<10;j++));
do
[ $j -eq 5 ] && break 2
echo $j
done
echo ---------------------------
done

参考:continue-break

(8) 循环控制 shift 命令

shift [n] 用于将参量列表 list 左移指定次数,缺省为左移一次。 参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。while 循环遍历位置参量列表时,常用到 shift。

示例:

代码语言:javascript
复制
#!/bin/bash
#step through all the positional parameters
until [ -z "$1" ]
do
echo "$1"
shift
done
echo
./shift.sh a b c d e f g h
————————————————————
#!/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"

(9) while read 特殊用法

while 循环的特殊用法,遍历文件或文本的每一行。

格式:

代码语言:javascript
复制
while read line;
do
循环体
done < /PATH/FROM/FILE

示例:

代码语言:javascript
复制
#!/bin/bash
WARNING=80
MAIL=root@gongboyi.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

(10) select 循环与菜单

格式:

代码语言:javascript
复制
select variable in list;
do
循环体命令
done

示例:

代码语言:javascript
复制
#!/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.11 Shell 函数

(1) 函数介绍

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

(2) 函数管理

① 定义函数

语法一:

代码语言:javascript
复制
func_name () { 

...函数体...
}

语法二:

代码语言:javascript
复制
function func_name { 

...函数体...
}

语法三:

代码语言:javascript
复制
function func_name () { 

...函数体...
}
② 查看函数
代码语言:javascript
复制
#查看当前已定义的函数名
declare -F
#查看当前已定义的函数定义
declare -f
#指定查看当前已定义的函数名
declare -F func_name
#指定查看当前已定义的函数名定义
declare -f func_name
③ 删除函数
代码语言:javascript
复制
unset func_name

(3) 函数调用

函数的调用方式:

代码语言:javascript
复制
可在交互式环境下定义函数
可将函数放在脚本文件中作为它的一部分
可放在只包含函数的单独文件中

函数的生命周期:

代码语言:javascript
复制
被调用时创建,返回时终止

调用的概念:

代码语言:javascript
复制
函数只有被调用才会执行,通过给定函数名调用函数,函数名出现的地方,会被自动替换为函数
代码。
① 交互式环境调用函数
代码语言:javascript
复制
[root@c7-1 ~]#dir () { 

> ls -l
> }
[root@c7-1 ~]#dir
总用量 4
-rw-r--r-- 1 root root 187 9月   8 17:47 1.sh
② 在脚本中定义及使用函数

函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至 shell 首次发现它后才能使用,调用函数仅使用其函数名即可。

代码语言:javascript
复制
#!/bin/bash
function fun () { 

echo "hello world!"
}
#通过函数名调用函数
fun
————————————————————
#!/bin/bash
function test { 

read -p "请输入数字:" num
return $[$num*2]
}
test
echo $?
————————————————————
#!/bin/bash
sum () { 

read -p "请输入第一个参数:" NUM1
read -p "请输入第二个参数:" NUM2
echo -e "\033[32m你输入的两个数为:$NUM1 和 $NUM2\033[0m"
SUM=$(($NUM1+$NUM2))
echo -e "\033[31m两个数之和为:$SUM\033[0m"
}
sum
—————————————————————
#!/bin/bash
#函数调用函数
# local 一般用于局部变量声明
fa () { 

if [ $1 -eq 1 ];then
echo 1
else
local tp=$[ $1 - 1 ]
local res=$(fa $tp)
echo $[ $1 * $res ]
fi
}
read -p "请输入:" num
res=$(fa $num)
echo $res
————————————————————
#!/bin/bash
#修改本地 repo 源
#请事先挂载光盘镜像,VMware设置里选择已连接
function repobackup { 

cd /etc/yum.repos.d
mkdir repo.bak
mv *.repo repo.bak
mount /dev/sr0 /mnt > /dev/null
}
makelocalrepo () { 

echo -e ' [local] name=local baseurl=file:///mnt enabled=1 gpgcheck=0' > local.repo
}
uselocalrepo () { 

yum clean all > /dev/null
yum makecache > /dev/null
yum -y install httpd > /dev/null
systemctl start httpd
}
#-----main-----#
repobackup
makelocalrepo
uselocalrepo
③ 使用函数文件
  • 可以将经常使用的函数存入一个单独的函数文件,然后将函数文件载入 shell,再进行调用函数
  • 文件名可任意选取,但最好与相关任务有某种联系,例如:functions
  • 一旦函数文件载入 shell,就可以在命令行或脚本中调用函数。可以使用 delcare -f 或 set 命令查看所有定义的函数,其输出列表包括已经载入 shell 的所有函数
  • 若要改动函数,首先用 unset 命令从 shell 中删除函数,改动完毕后,再重新载入此文件

实现函数文件的过程:

代码语言:javascript
复制
创建函数文件,只存放函数的定义
在 shell 脚本或交互式 shell 中调用函数文件,格式如下:
. filename 或 source filename

示例:

代码语言:javascript
复制
[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
}

(4) 函数返回值

函数的执行结果返回值:

代码语言:javascript
复制
使用 echo 等命令进行输出
函数体中调用命令的输出结果

函数的退出状态码:

代码语言:javascript
复制
默认取决于函数中执行的最后一条命令的退出状态码
自定义退出状态码,其格式为:
return 从函数中返回,用最后状态命令决定返回值
return 0 无错误返回
return 1-255 有错误返回

(5) 环境函数

类拟于环境变量,也可以定义环境函数,使子进程也可使用父进程定义的函数。 定义环境函数:

代码语言:javascript
复制
export -f function_name
declare -xf function_name

查看环境函数:

代码语言:javascript
复制
export -f
declare -xf

(6) 函数参数

函数可以接受参数:

代码语言:javascript
复制
传递参数给函数,在函数名后面以空白分隔给定参数列表即可,如:func arg1 arg2 ...
在函数体中当中,可使用$1, $2, ...调用这些参数。还可以使用$@, $*, $#等特殊变量

(7) 函数变量

变量作用域:

代码语言:javascript
复制
普通变量:只在当前 shell 进程有效,为执行脚本会启动专用子 shell 进程。因此,本地变量的作用范围是当前 shell 脚本程序文件,包括脚本中的函数
环境变量:当前 shell 和子 shell 有效
本地变量:函数的生命周期结束时变量被自动销毁

注意:

代码语言:javascript
复制
如果函数中定义了普通变量,且名称和局部变量相同,则使用本地变量
由于普通变量和局部变量会冲突,建议在函数中只使用本地变量

在函数中定义本地变量的方法:

代码语言:javascript
复制
local NAME=VALUE

(8) 函数递归

函数递归:函数直接或间接调用自身,注意递归层数,可能会陷入死循环。 递归示例:

代码语言:javascript
复制
阶乘是基斯顿·卡曼于 1808 年发明的运算符号,是数学术语,一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且有 0 的阶乘为 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)!

示例:

代码语言:javascript
复制
#!/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 进程的无限循环,实质是一个简单的递归程序。由于程序是递归的,如果没有任何限制,这会导致这个简单的程序迅速耗尽系统里面的所有资源。 参考: fork 炸弹 函数实现:

代码语言:javascript
复制
:(){ 
 :|:& };:
bomb() { 
 bomb | bomb & }; bomb

脚本实现:

代码语言:javascript
复制
cat bomb.sh
#!/bin/bash
./$0|./$0&

2.12 一些脚本相关工具

(1) 信号捕捉 trap

代码语言:javascript
复制
trap '触发指令' 信号
进程收到系统发出的指定信号后,将执行自定义指令,而不会执行原操作
trap '' 信号
忽略信号的操作
trap '-' 信号
恢复原信号的操作
trap -p
列出自定义信号操作
trap finish EXIT
当脚本退出时,执行 finish 函数

示例:

代码语言:javascript
复制
#!/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

(2) 创建临时文件 mktemp

mktemp 命令用于创建并显示临时文件,可避免冲突。 格式:

代码语言:javascript
复制
mktemp [OPTION]... [TEMPLATE]

常见选项:

代码语言:javascript
复制
-d 创建临时目录
-p DIR或--tmpdir=DIR 指明临时文件所存放目录位置

示例:

代码语言:javascript
复制
mktemp /tmp/testXXX
tmpdir=`mktemp –d /tmp/testdirXXX`
mktemp --tmpdir=/testdir testXXXXXX

(3) 安装复制文件 install

install 命令格式:

代码语言:javascript
复制
install [OPTION]... [-T] SOURCE DEST 单文件
install [OPTION]... SOURCE... DIRECTORY
install [OPTION]... -t DIRECTORY SOURCE...
install [OPTION]... -d DIRECTORY...创建空目录

选项:

代码语言:javascript
复制
-m MODE,默认755
-o OWNER
-g GROUP

示例:

代码语言:javascript
复制
install -m 700 -o wang -g admins srcfile desfile
install -m 770 -d /testdir/installdir 

(4) 交互式转化批处理工具 expect

  expect 主要应用于自动化交互式操作的场景,借助 expect 处理交互的命令,可以将交互过程如:ssh 登录,ftp 登录等写在一个脚本上,使之自动完成。尤其适用于需要对多台服务器执行相同操作的环境中,可以大大提高系统管理人员的工作效率。

expect 语法:

代码语言:javascript
复制
expect [选项] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]

常见选项:

代码语言:javascript
复制
-c:从命令行执行expect脚本,默认expect是交互执行的
-d:可以输出调试信息

expect 中相关命令:

代码语言:javascript
复制
spawn	启动新的进程
expect	从进程接收字符串
send	用于向进程发送字符串
interact		允许用户交互
exp_continue	匹配多个字符串在执行动作后加此命令

详细使用参考:交互式转化批处理工具 expect

2.13 数组

(1) 数组介绍

变量:存储单个元素的内存空间。 数组:存储多个元素的连续的内存空间,相当于多个变量的集合。 数组名和索引:

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

(2) 声明数组

代码语言:javascript
复制
#普通数组可以不事先声明,直接使用
declare -a ARRAY_NAME
#关联数组必须先声明,再使用
declare -A ARRAY_NAME
#两者不可相互转换

(3) 数组赋值

① 一次只赋值一个元素
代码语言:javascript
复制
ARRAY_NAME[INDEX]=VALUE

示例:

代码语言:javascript
复制
weekdays[0]="Sunday"
weekdays[4]="Thursday"
② 一次赋值全部元素
代码语言:javascript
复制
ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)

示例:

代码语言:javascript
复制
title=("ceo" "coo" "cto")
num=({ 
0..10})
alpha=({ 
a..g})
file=( *.sh )
③ 只赋值特定元素
代码语言:javascript
复制
ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
④ 交互式数组值对赋值
代码语言:javascript
复制
read -a ARRAY

(4) 显示所有数组

代码语言:javascript
复制
declare -a

示例:

代码语言:javascript
复制
[root@aliyunhost01~]#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")'

(5) 引用数组

① 引用数组元素
代码语言:javascript
复制
${ARRAY_NAME[INDEX]}
#如果省略[INDEX]表示引用下标为0的元素

示例:

代码语言:javascript
复制
[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]}
② 引用数组所有元素
代码语言:javascript
复制
${ARRAY_NAME[*]}
${ARRAY_NAME[@]}

示例:

代码语言:javascript
复制
[root@centos8 ~]#echo ${title[@]}
ceo coo cto
[root@centos8 ~]#echo ${title[*]}
ceo coo cto
③ 数组的长度,即数组中元素的个数
代码语言:javascript
复制
${ 
#ARRAY_NAME[*]}
${ 
#ARRAY_NAME[@]}

示例:

代码语言:javascript
复制
[root@centos8 ~]#echo ${#title[*]}
3

(6) 删除数组

① 删除数组中的某个元素
代码语言:javascript
复制
unset ARRAY[INDEX]

示例:

代码语言:javascript
复制
[root@centos8 ~]#echo ${title[*]}
ceo coo cto
[root@centos8 ~]#unset title[1]
[root@centos8 ~]#echo ${title[*]}
ceo cto
② 删除整个数组
代码语言:javascript
复制
unset ARRAY

示例:

代码语言:javascript
复制
[root@centos8 ~]#unset title
[root@centos8 ~]#echo ${title[*]}
[root@centos8 ~]#

(7) 数组数据处理

① 数组切片
代码语言:javascript
复制
${ARRAY[@]:offset:number}
offset #要跳过的元素个数
number #要取出的元素个数
#取偏移量之后的所有元素
{ 
ARRAY[@]:offset}

示例:

代码语言:javascript
复制
[root@centos8 ~]#num=({0..10})
[root@centos8 ~]#echo ${num[*]:2:3}
2 3 4
[root@centos8 ~]#echo ${num[*]:6}
6 7 8 9 10
② 向数组中追加元素
代码语言:javascript
复制
ARRAY[${ 
#ARRAY[*]}]=value
ARRAY[${ 
#ARRAY[@]}]=value

示例:

代码语言:javascript
复制
[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

(8) 关联数组

代码语言:javascript
复制
declare -A ARRAY_NAME
ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2‘...)
注意:关联数组必须先声明再调用

示例:

代码语言:javascript
复制
[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

关联数组参考: Shell 关联数组 Shell 中的关联数组,获取数组索引

(9) 数组脚本示例

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

代码语言:javascript
复制
#!/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 结尾的文件。统计出其下标为偶数的文件中的行数之和

代码语言:javascript
复制
#!/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"

范例:shell 脚本实现冒泡排序

代码语言:javascript
复制
#!/bin/bash
#a test about sort
echo "please input a number list"
read -a arrs
for((i=0;i<${#arrs[@]};i++)){ 

for((j=0;j<${#arrs[@]}-1;j++)){ 

if [[ ${arrs[j]} -gt ${arrs[j+1]} ]];then
tmp=${arrs[j]}
arrs[j]=${arrs[j+1]}
arrs[j+1]=$tmp
fi
}
}
echo "after sort"
echo ${arrs[@]}

数组参考: Shell 数组 Shell 数组操作 Shell 脚本数组用法小结 Shell 数组(详细)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022年9月26日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 1. shell 概述
    • 1.1 shell 和 shell 脚本
      • 1.2 Shell 脚本应用场景
        • 1.3 Linux 中的 shell 类型
          • 1.4 脚本实例
          • 2. shell 脚本编程基础
            • 2.1 脚本调试
              • 2.2 脚本执行方法
                • 2.3 管道符号 |
                  • 2.4 交互式硬件设备
                    • 2.5 重定向操作
                      • 2.6 变量
                        • (1) 变量的赋值与引用
                        • (2) 变量类型
                        • (3) 变量的查看与删除
                        • (4) 变量的赋值运算
                      • 2.7 算数运算
                        • (1) 实现算数运算的方式
                        • (2) 增强型赋值
                        • (3) 逻辑运算
                        • (4) 随机数生成器变量 – $RANDOM
                        • (5) “空” 设备 – /dev/null
                        • (6) “零” 设备 – /dev/zero
                        • (7) 随机数生成器设备 – /dev/random /dev/urandom
                      • 2.8 echo -n/-e 的用法
                        • 2.9 条件测试命令
                          • 2.10 流程控制
                            • (1) if 条件语句
                            • (2) case 多分支语句
                            • (3) for 循环
                            • (4) while 循环
                            • (5) until 循环
                            • (6) 循环控制语句 continue
                            • (7) 循环控制语句 break
                            • (8) 循环控制 shift 命令
                            • (9) while read 特殊用法
                            • (10) select 循环与菜单
                          • 2.11 Shell 函数
                            • (1) 函数介绍
                            • (2) 函数管理
                            • (3) 函数调用
                            • (4) 函数返回值
                            • (5) 环境函数
                            • (6) 函数参数
                            • (7) 函数变量
                            • (8) 函数递归
                          • 2.12 一些脚本相关工具
                            • (1) 信号捕捉 trap
                            • (2) 创建临时文件 mktemp
                            • (3) 安装复制文件 install
                            • (4) 交互式转化批处理工具 expect
                          • 2.13 数组
                            • (1) 数组介绍
                            • (2) 声明数组
                            • (3) 数组赋值
                            • (4) 显示所有数组
                            • (5) 引用数组
                            • (6) 删除数组
                            • (7) 数组数据处理
                            • (8) 关联数组
                            • (9) 数组脚本示例
                        相关产品与服务
                        测试服务
                        测试服务 WeTest 包括标准兼容测试、专家兼容测试、手游安全测试、远程调试等多款产品,服务于海量腾讯精品游戏,涵盖兼容测试、压力测试、性能测试、安全测试、远程调试等多个方向,立体化安全防护体系,保卫您的信息安全。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档