重点:
- 条件测试。
- read。
- Shell 环境配置。
- case。
- for。
- find。
- xargs。
- gzip,bzip2,xz。
- tar。
- sed。
1)编程基础
Linus 说:Talk is cheap, show me the code。
1.1)程序组成
- 程序:算法 + 数据结构
- 数据:是程序的核心
- 数据结构:数据在计算机中的类型和组织方式
- 算法:处理数据的方式
1.2)程序编程风格
实际上面向对象编程(OOP)和面向过程编程(Procedural Programming)并不是绝对的说辞,而是两种不同的编程范式。
几乎所有编程语言都可以在某种程度上支持这两种编程范式。
面向 过程 语言
- 做一件事情,排出个步骤,第一步干什么,第二步干什么,如果出现情况 A,做什么处理,如果出现了情况 B,做什么处理。
- 问题规模小,可以步骤化,按部就班处理。
- 以指令为中心,数据服务于指令。
- C,shell
面向 对象 语言
- 将编程看成是一个事物,对外界来说,事物是直接使用的,不用关心事物内部的情况。而编程就是设置事物能够完成功能。
- 一种认识世界、分析世界的方法论。将万事万物抽象为各种对象。
- 类是抽象的概念,是万事万物的抽象,是一类事物的共同特征的集合
- 对象是类的具象,是一个实体
- 问题规模大,复杂系统
- 以数据为中心,指令服务于数据
- Java,C#,Python,golang 等
1.3)编程语言
编程语言排名链接
https://www.tiobe.com/tiobe-index/
TIBOE 2021 年 1 月的最新编程语言流行度排名
计算机:运行二进制指令
编程语言:人与计算机之间 交互的语言。
分为两种:低级语言 和 高级语言。
低级编程 语言:
- 机器语言:二进制的 0 和 1 的序列,称为 机器指令。与自然语言差异太大,难懂、难写。
- 汇编语言:用一些助记符号替代机器指令,称为 汇编语言。
- 如:ADD A,B 将寄存器A的数与寄存器B的数相加得到的数放到寄存器A中。
- 汇编语言写好的程序需要汇编程序转换成机器指令。
- 汇编语言稍微好理解,即机器指令对应的助记符,助记符更接近自然语言。
高级编程 语言:
- 编译型语言:高级语言 --> 基于编译器 --> 编译成机器二进制代码文件 --> 执行。[ 整体编译执行 ]
- 如:C,C++。
- 解释型语言:高级语言 --> 执行 --> 基于解释器 --> 解释成机器二进制代码。 [ 逐行解释执行 ]
- 如:Shell,Python,php,JavaScript,perl。
编译型语言 的代码在执行前需要通过一个称为编译器的特殊程序进行编译。
[ 例如:Windows 客户端工具 EXE ]
- 预先编译:源代码在执行前经过编译,生成目标代码,执行时直接运行目标代码,无需再次编译。
- 性能高:由于编译器将代码转换为机器码,执行效率通常较高。
- 跨平台:需要为每个平台编译不同的目标代码,因此可以实现跨平台运行。
解释型语言 的代码在执行时并不是直接转换为机器码或字节码,而是通过解释器逐行解释并执行。
[ 例如:Python Shell 脚本 1.py 1.sh ]
- 即时解释:源代码在执行时逐行解释,并实时执行,无需预先编译。
- 相对较慢:因为解释器需要逐行解释代码,相比编译型语言,执行效率较低。
- 跨平台:由于解释器的存在,通常不需要重新编译,因此可以实现跨平台运行。
总结:
编译型语言在执行前将代码编译成机器码,因此执行速度较快,但需要为不同平台编译不同的目标代码。
解释型语言在执行时逐行解释代码,因此执行速度相对较慢,但具有跨平台的优势。不同的语言适用于不同的场景,开发者可以根据需求选择合适的编程语言。
1. 低级编程语言 与 高级编程语言 的区别
2. 编译器 和 解释器 的区别
1.4)编程逻辑处理方式
三种处理逻辑
- 顺序执行:程序 按从上到下顺序执行
- 选择执行:程序执行过程中,根据条件的不同,进行选择不同分支继续执行
- 循环执行:程序执行过程中需要 重复执行多次某段语句
2)Shell 脚本语言的基本用法
2.1)Shell 脚本的用途
- 将简单的命令组合 完成复杂的工作,自动化执行命令,提高工作效率。
- 减少手工命令的重复输入,一定程度上避免人为错误。
- 将软件或应用的安装及配置 实现标准化。
- 用于实现日常性的,重复性的运维工作,如:文件打包压缩备份,监控系统运行状态并实现告警等。
2.2)Shell 脚本基本结构
Shell 脚本编程:是基于过程式、解释执行的语言。
编程语言的基本结构:
- 各种系统 命令的组合
- 数据存储:变量、数组
- 表达式:a + b
- 控制语句:if
Shell 脚本:包含一些命令或声明,并符合一定格式的文本文件。
格式要求:首行 shebang 机制。
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
2.3)Shell 脚本创建过程
第一步:使用文本编辑器来 编写脚本文件。
第一行:必须包括 Shell 声明序列:#!
示例:
#!/bin/bash
第二步:加执行权限
给予执行权限,在命令行上指定脚本的绝对或相对路径。
chmod +x
第三步:运行脚本
直接运行解释器,将脚本作为解释器程序的参数运行 bash
bash
[ 演示 ]
1. 编写脚本文件
vim hello.sh
#!/bin/bash
echo 'Hello world'
2. 赋予执行权限
chmod +x hello.sh
3. 调用 bash 执行脚本
bash hello.sh
2.4)Shell 脚本 三种执行方式
1)将脚本放置 PATH 路径
echo $PATH
3. 再次输入 hello.sh
系统基于调用命令的方式执行 hello.sh
hello.sh
2)基于 软链接 方式
[ 将脚本文件引用到 /usr/local/bin 目录 ]
ln -s "脚本绝对路径" "软链接存放目录"
ln -s /root/hello.sh /usr/local/bin
ll /usr/local/bin
系统成功基于调用 PATH 路径命令的方式执行 hello.sh
hello.sh
3)基于 脚本绝对路径\相对路径 运行脚本
/root/hello.sh
./hello.sh
4)无执行权限的脚本,如何执行
bash hello.sh
cat hello.sh | bash
# 执行远程主机的脚本
curl -s https://www.wangxiaochun.com/testdir/hello.sh | bash
wget -qO - https://www.wangxiaochun.com/testdir/hello.sh | bash
2.5)Shell 脚本注释规范
1、第一行一般为 调用使用的语言
2、程序名,避免更改文件名为无法找到正确的文件
3、版本号
4、更改后的时间
5、作者相关信息
6、该程序的作用,及注意事项
7、最后是各版本的 更新简要说明
备份脚本
mkdir /data
#!/bin/bash
#
# ********************************************************************
# Author: wangjun
# QQ: 1281570031
# Date: 2023-08-04
# FileName: backup.sh
# URL: https://linux.wuhanjiayou.cn
# Description: The test script
# Copyright (C): 2023 All rights reserved
# ********************************************************************
echo -e "\033[1;32mStarting backup...\033[0m"
sleep 2
cp -av /etc/ /data/etc`date +%F`/
echo -e "\033[1;32mBackup is finished\033[0m"
2.6)Shell 脚本调试
总结:脚本错误常见的有三种
命令错误,默认后续的命令还会继续执行,用 bash -n 无法检查出来 ,可以使用 bash -x 进行观察。
语法错误,会导致后续的命令不继续执行,可以用 bash -n 检查错误,提示的出错行数不一定是准确的。
逻辑错误:只能使用 bash -x 进行观察。
2.6.1)命令错误
默认后续的命令 还会继续执行
并且脚本执行报错,会提示哪个位置出错
[00:29:23 root@blog ~]# cat hello.sh
#!/bin/bash
echo 'Hello world'
hosname
echo '123'
# 定位到第 3 行
vim +3 hello.sh
2.6.2)语法错误
会导致后续的命令不继续执行
[00:36:49 root@blog ~]# cat hello.sh
#!/bin/bash
echo 'Hello world'
hostname
cat > /data/app.conf <<EOF
line1
line2
EoF
echo '123'
[ 定位语法错误 ]
检测脚本中的 语法错误
无法检查出 命令错误,不真正执行脚本。
bash -n hello.sh
[ 注意该行号信息不一定准确 仅能告知大概位置 ]
修正语法后...
bash hello.sh
2.6.3)逻辑错误
使用 bash -x 进行观察 [ 调试执行 ]
运行 "bash -x hello.sh" 会以调试模式执行 "hello.sh" 脚本,显示每个命令在执行前的扩展状态,方便排查错误和调试。
bash -x hello.sh
2.6.4)案例展示
curl -s https://www.wangxiaochun.com/testdir/system_info.sh | bash
3)变量
变量 表示命名的内存空间,将数据放在内存空间中,通过变量名引用,获取数据。
变量类型:
内置变量如:PS1,PATH,UID,HOSTNAME,$$,BASHPID,PPID,$?,HISTSIZE。
用户自定义变量
不同的变量存放的数据不同,决定了以下:
- 数据存储方式
- 参与的运算
- 表示的数据范围
变量数据类型
- 字符
- 数值:整型、浮点型,bash 不支持浮点数
编程语言分类
静态和动态语言
- 静态编译语言:使用变量前,需先声明变量类型,之后类型不能改变,在编译时检查,如:Java,C
- 动态编译语言:无需事先声明,可随时改变类型,如:Bash,Python
强类型和弱类型语言
- 强类型语言:不同类型数据操作必须经过强制转换才同一类型才能运算,如 Java , C#,Python。
- 参考以下 python 代码弱类型语言:语言的运行时会隐式做数据类型转换。无须指定类型,默认均为字符型;参与运算会自动进行隐式类型转换;变量无须事先定义可直接调用。如:Bash ,php,Javascript。
- print('magedu'+ 10) 提示出错,不会自动转换类型
- print('magedu'+str(10)) 结果为magedu10,需要显示转换类型
3.1)Shell 变量命名
命名要求
- 区分大小写
- 不要使用系统自带的内置变量或系统命令等
- 如:hostname,ls,who,PATH 等
- 只能使用数字、字母及下划线,且不能以数字开头,
- 注意:不支持短横线 "-",和主机名相反。
命令习惯
- 见名知义,用英文单词命名,并体现出实际作用,不要用简写
- 变量名大写
- 局部变量小写
- 函数名小写
- 部分 企业命名 规范
- 大驼峰StudentFirstName
- 小驼峰studentFirstName
- 下划线student_name
变量定义和引用
变量的生效范围等标准划分变量类型
- 普通变量:生效范围为当前 shell 进程;对当前 shell 之外的其它shell进程,包括当前 shell 的子 shell 进程均无效。
- 环境变量:生效范围为当前 shell 进程及其子进程。
- 本地变量:生效范围为当前 shell 进程中某代码片断,通常指函数。
3.2)变量赋值
# 变量赋值的正确写法
name='wangj'
# 变量赋值的错误写法
# 这种写法用于比较值是否相同: 如 if [ "$name" = "wangj" ]
name = 'wangj'
value 还可以是以下多种形式
[ 注意:变量赋值是临时生效,当退出终端后,变量会自动删除,无法持久保存,脚本中的变量会随着脚本结束,也会自动删除 ]
直接字串: name='root'
变量引用: name="$USER"
命令引用: name=`COMMAND` 或者 name=$(COMMAND)
3.3)变量引用
弱引用和强引用
- "$name" 强引用,其中的变量引用 会被替换 为变量值。
- '$name' 弱引用,其中的变量引用 不会被替换 为变量值,而保持原字符串。
$name
${name}
3.4)示范案例
[ 示范 1 ]
变量的各种赋值方式和引用
# 变量赋值
TITLE='Linux 运维工程师'
# 变量引用
echo $TITLE
# 强引用
echo "Im a $TITLE"
# 弱引用
echo 'Im a $TITLE'
[ 示范 2 ]
花括号的用途
# 变量赋值
NAME='wangj'
TITLE='Linux'
# 变量引用
# 以 : 冒号作为变量值分隔符
echo $NAME:$TITLE
# 变量引用
# 以 _ 下划线作为变量值分隔符
echo $NAME_$TITLE
# 使用花括号
echo ${NAME}_$TITLE
# 取消赋值
unset NAME
[ 示范 3 ]
调用 命令执行结果 为变量值
NAME=`whoami`
echo $NAME
SDA1_UUID=`lsblk -f | grep root | awk '{print $3}'`
echo $SDA1_UUID
LOG=`ls /var/log/messages*`
echo $LOG
[ 示范 4 ]
变量追加值 TITLE+=:wang
TITLE='CTO'
TITLE+=:wang
echo $TITLE
[ 示范 5 ]
利用变量 实现动态命令
CMD=hostname
$CMD
[ 示范 6 ]
#!/bin/bash
#
#********************************************************************
#Author: wangxiaochun
#QQ: 29308620
#Date: 2019-12-23
#FileName: systeminfo.sh
#URL: http://www.magedu.com
#Description: Show system information
#Copyright (C): 2019 All rights reserved
#********************************************************************
RED="\E[1;31m"
GREEN="echo -e \E[1;32m"
END="\E[0m"
$GREEN----------------------Host systeminfo--------------------$END
echo -e "HOSTNAME: $RED`hostname`$END"
#echo -e "IPADDR: $RED` ifconfig eth0|grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' |head -n1`$END"
echo -e "IPADDR: $RED` hostname -I`$END"
echo -e "OSVERSION: $RED$PRETTY_NAME$END"
echo -e "KERNEL: $RED`uname -r`$END"
echo -e "CPU: $RED`lscpu|grep '^Model name'|tr -s ' '|cut -d : -f2`$END"
echo -e "MEMORY: $RED`free -h|grep Mem|tr -s ' ' : |cut -d : -f2`$END"
echo -e "DISK: $RED`lsblk |grep '^sd' |tr -s ' ' |cut -d " " -f4`$END"
$GREEN---------------------------------------------------------$END
显示已定义的所有变量
set
set | grep -n TITLE
3.5)优化脚本
#!/bin/bash
#
# ********************************************************************
# Author: wangjun
# QQ: 1281570031
# Date: 2023-08-04
# FileName: backup.sh
# URL: https://blog.wuhanjiayou.cn
# Description: The test script
# Copyright (C): 2023 All rights reserved
# ********************************************************************
COLOR='echo -e \E[1;35m'
END='\E[0m'
BACKUP_DIR='/backup'
SRC_DIR='/etc'
DATE=`date +%F`
${COLOR} Starting backup... ${END}
sleep 2
cp -av ${SRC_DIR} ${BACKUP_DIR}${SRC_DIR}_$DATE
${COLOR} Backup is finished ${END}
3.6)练习题
1、编写脚本 systeminfo.sh,显示当前主机系统信息,包括:主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大小。
#!/bin/bash
# ANSI颜色代码
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # 恢复默认颜色
# 获取主机名
hostname=$(hostname)
# 获取IPv4地址
ipv4_address=$(hostname -I | cut -d' ' -f1)
# 获取操作系统版本
os_version=$(cat /etc/os-release | grep "PRETTY_NAME" | cut -d'=' -f2 | tr -d '"')
# 获取内核版本
kernel_version=$(uname -r)
# 获取CPU型号
cpu_model=$(lscpu | grep "Model name" | cut -d':' -f2 | xargs)
# 获取内存大小
memory_size=$(free -h | awk '/Mem/ {print $2}')
# 获取硬盘大小
disk_size=$(df -h / | awk 'NR==2 {print $2}')
# 显示系统信息并高亮显示
echo -e "主机名: ${RED}$hostname${NC}"
echo -e "IPv4地址: ${GREEN}$ipv4_address${NC}"
echo -e "操作系统版本: ${YELLOW}$os_version${NC}"
echo -e "内核版本: ${GREEN}$kernel_version${NC}"
echo -e "CPU型号: ${YELLOW}$cpu_model${NC}"
echo -e "内存大小: ${GREEN}$memory_size${NC}"
echo -e "硬盘大小: ${YELLOW}$disk_size${NC}"
2、编写脚本 backup.sh,可实现每日将 /etc/ 目录备份到 /backup/etcYYYY-mm-dd中
#!/bin/bash
# 源目录
source_dir="/etc"
# 备份目录
backup_dir="/backup"
# 获取当前日期, 格式为YYYY-mm-dd
current_date=$(date +%Y-%m-%d)
# 创建备份目录, 如果不存在
if [ ! -d "$backup_dir" ]; then
mkdir -p "$backup_dir"
fi
# 备份文件的完整路径
backup_file="$backup_dir/etc$current_date.tar.gz"
# 执行备份
tar -czvf "$backup_file" "$source_dir"
# 检查备份是否成功
if [ $? -eq 0 ]; then
echo "备份成功:$backup_file"
else
echo "备份失败"
fi
3、编写脚本 disk.sh,显示当前硬盘分区中空间利用率最大的值
#!/bin/bash
# ANSI颜色代码
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # 恢复默认颜色
# 使用df命令获取硬盘分区信息,将其按利用率降序排序,并提取最大利用率的行
max_usage_partition=$(df -h | awk '{print $5 " " $6}' | sort -k1 -n -r | head -n 1)
# 提取最大利用率和分区路径
max_usage=$(echo $max_usage_partition | awk '{print $1}')
partition_path=$(echo $max_usage_partition | awk '{print $2}')
# 显示最大利用率的分区信息并高亮显示
echo -e "最大利用率分区: ${YELLOW}$partition_path${NC},利用率: ${RED}$max_usage${NC}"
4、编写脚本 links.sh,显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序
#!/bin/bash
# 使用netstat命令获取连接信息,筛选出IPv4地址,按连接数降序排序,并提取唯一的远程主机IP和连接数
connection_info=$(netstat -n -t | grep ESTABLISHED | grep -oE "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" | sort | uniq -c | sort -nr)
# 显示每个远程主机的IPv4地址和连接数
echo "远程主机 IPv4 地址 和 连接数:"
echo "$connection_info"
4)环境变量
环境变量
- 可以 使子进程(包括孙子进程)继承父进程的变量,但是无法让父进程使用子进程的变量。
- 一旦子进程 修改 从父进程继承的变量,将会新的值传递给孙子进程。
- 一般只在 系统配置文件 中使用,在脚本中较少使用。
# 输出当前终端的进程 PID
echo $BASHPID
# 在当前终端执行进程
sleep 10000
左边的进程 即是 右边进程的 父进程
常规 变量
# 父进程定义变量
NAME='wangj'
echo $NAME
# 子进程并不会继承父进程的变量信息
bash
echo $NAME
环境 变量
声明与赋值
"声明并赋值" 环境变量
export NAME=wangj
# 引用变量
echo $NAME
# 子进程会继承父进程的环境变量
bash
echo $NAME
显示所有环境变量
env
export
printenv
面试题:
查看 指定进程 引用的环境变量
# 用法
cat /proc/$PID/environ
# 示例
cat /proc/14798/environ
cat /proc/14798/environ | tr '\0' '\n'
5)只读变量
只读变量:只能声明定义,后续不能修改和删除,即常量。
# 声明只读变量
readonly AGE=18
# 引用变量
echo $AGE
# 修改变量
AGE=20
# 删除变量
unset AGE
只读变量确实无法在当前会话中被修改或删除。
但由于常规变量是在终端的父进程上赋值的,所以在 退出终端后重新打开 时,这些变量会被重置为它们的初始值。
如果您希望在终端会话之间保留变量的值,可以考虑将它们保存在配置文件中,如 .bashrc 。这样每次打开终端时,这些变量会被自动加载,而不会被重置。
查看只读变量
readonly -p