一、Shell脚本进阶
1. while read
while中有一种特殊的用法,while read line 可以从文本中逐行读取相关的内容,然后存储到一个临时变量line中,后续就可以逐行对文本内容进行操作。
实践:
使用while read line和/etc/passwd,计算用户id总和。
root@ubuntu2404-016:~# sum=0
root@ubuntu2404-016:~# while read line; do
> sum=$((sum + $(echo $line | cut -d: -f3)))
> done < /etc/passwd
root@ubuntu2404-016:~# echo "用户ID总和为:$sum"
用户ID总和为:88346
root@ubuntu2404-016:~#
2. 索引数组和关联数组
索引数组:以数字为索引,下标从0开始,不需要声明,直接定义。
定义方式:
- 单行定义:array_name=(value1 value2 … valuen) 元素之前使用空格隔
- 多行定义:通过换行分隔元素
- 单元素定义:array_name[index]=value 索引可以不连续,支持稀疏格式
实践:
# 单行定义
root@ubuntu2404-016:~# number_list=(1,2,3,4)
root@ubuntu2404-016:~# echo "第一个数:${number_list[0]}"
第一个数:1,2,3,4
root@ubuntu2404-016:~# echo "所有数:${number_list[@]}"
所有数:1,2,3,4
root@ubuntu2404-016:~#
# 多行定义
root@ubuntu2404-016:~# number_list1=(
> 1
> 3
> 5
> 7)
root@ubuntu2404-016:~# echo "所有数:${number_list1[@]}"
所有数:1 3 5 7
root@ubuntu2404-016:~#
# 单元素定义
root@ubuntu2404-016:~# single_element[0]="element1"
root@ubuntu2404-016:~# single_element[3]="element4"
root@ubuntu2404-016:~# echo "索引为0的元素:${single_element[0]}"
索引为0的元素:element1
root@ubuntu2404-016:~# echo "索引为2的元素:${single_element[2]}"
索引为2的元素:
root@ubuntu2404-016:~# echo "所有元素:${single_element[@]}"
所有元素:element1 element4
root@ubuntu2404-016:~#
关联数组:以自定义的字符串为索引,需要先声明再使用。
语法:
-
声明:
declare -A arr_name
-
赋值:
arr_name["key"]=value
或arr_name=(["key1"]=value1 ["key2"]=value2)
实践:
# 声明并赋值
root@ubuntu2404-016:~# declare -A score
root@ubuntu2404-016:~# score=(["english"]=85 ["math"]=90 ["chinese"]=95)
root@ubuntu2404-016:~# echo "语文成绩:${score["chinese"]}"
语文成绩:95
root@ubuntu2404-016:~# echo "所有科目:${!score[@]}"
所有科目:english math chinese
root@ubuntu2404-016:~# echo "所有成绩:${score[@]}"
所有成绩:85 90 95
root@ubuntu2404-016:~#
3. 字符串处理
Shell 提供丰富的字符串操作,包括截取、替换、转换等,无需依赖外部工具,直接通过变量语法实现。
1. 字符串截取
通过#(从左匹配)、%(从右匹配)实现,分为 “最短匹配” 和 “最长匹配”。
- 语法:
-
${string#*pattern}:从左删除第一个pattern及其左侧内容(最短匹配)
-
${string##*pattern}:从左删除最后一个pattern及其左侧内容(最长匹配)
-
${string%pattern*}:从右删除第一个pattern及其右侧内容(最短匹配)
-
${string%%pattern*}:从右删除最后一个pattern及其右侧内容(最长匹配)
-
- 实践:
file="/var/log/nginx/access.log"
# 截取文件名(从右删除第一个"/"及其左侧)
root@ubuntu2404-016:~/temp# echo "${file##*/}"
access.log
# 截取目录(从右删除第一个"/"及其右侧)
root@ubuntu2404-016:~/temp# echo "${file%/*}"
/var/log/nginx
# 截取后缀(从右删除第一个"."及其左侧)
root@ubuntu2404-016:~/temp# echo "${file##*.}"
log
root@ubuntu2404-016:~/temp#
2. 字符串替换
通过/实现部分或全部替换,支持开头 / 结尾匹配。
- 语法:
-
${string/old/new}:替换第一个old为new
-
${string//old/new}:替换所有old为new
-
${string/#old/new}:仅替换开头的old为new
-
${string/%old/new}:仅替换结尾的old为new
-
- 实践:
root@ubuntu2404-016:~/temp# str="apple, apple, banana"
root@ubuntu2404-016:~/temp#
root@ubuntu2404-016:~/temp# echo "${str/apple/APPLE}"
APPLE, apple, banana
root@ubuntu2404-016:~/temp# echo "${str//apple/APPLE}"
APPLE, APPLE, banana
root@ubuntu2404-016:~/temp# echo "${str/#apple/APPLE}"
APPLE, apple, banana
root@ubuntu2404-016:~/temp# echo "${str/%banana/PEAR}"
apple, apple, banana
root@ubuntu2404-016:~/temp#
3. 字符串转换
通过^^(转大写)和,(转小写)实现大小写转换。
-
语法:
-
${string^^}:全部转为大写
-
${string,}:全部转为小写
-
-
实践:
root@ubuntu2404-016:~/temp# text="Hello World"
root@ubuntu2404-016:~/temp# echo "${text^^}"
HELLO WORLD
root@ubuntu2404-016:~/temp# echo "${text,,}"
hello world
root@ubuntu2404-016:~/temp#
4. 高级变量使用
- 嵌套变量问题
Shell 默认不支持多层变量解析,例如{1..$n}
中的$n
无法直接解析为数字(需先解析$n,再解析{1…n})。
-
eval命令
-
功能:对命令或变量进行二次解析,支持多层嵌套解析。
-
语法:eval command(先解析command中的变量 / 命令,再执行)。
-
-
实践:动态生成序列
root@ubuntu2404-016:~/temp# n=5
# 直接使用{1..$n}无法解析,需用eval
root@ubuntu2404-016:~/temp# for i in $(eval echo {1..$n}); do
> echo $i
> done
1
2
3
4
5
root@ubuntu2404-016:~/temp#
- 实践:动态变量名赋值
root@ubuntu2404-016:~/temp# name="age"
root@ubuntu2404-016:~/temp# value=25
# 用eval实现"age=25"的动态赋值
root@ubuntu2404-016:~/temp# eval "$name=$value"
root@ubuntu2404-016:~/temp# echo $age
25
root@ubuntu2404-016:~/temp#
- 嵌套变量场景
当变量值中包含另一个变量时,eval可实现多层解析:
root@ubuntu2404-016:~/temp# a1=hello
root@ubuntu2404-016:~/temp# str="a1"
# 直接echo $str输出"a1"
root@ubuntu2404-016:~/temp# echo $str
a1
# 用eval解析为变量a1的值
root@ubuntu2404-016:~/temp# eval "echo \$$str"
hello
root@ubuntu2404-016:~/temp#
5. 求最大最小值
求10个随机数的最大值与最小值
root@ubuntu2404-016:~/temp# vim compare_nums_for.sh
# 设定大小比较
for ((i=0; i<10; i++)); do
nums[$i]=$RANDOM
# 设定最大值
if [ ${nums[$i]} -gt $max ]; then
max=${nums[$i]}
fi
# 设定最小值
if [ ${nums[$i]} -lt $min ]; then
min=${nums[$i]}
fi
done
echo "------------------------------"
echo "所有的随机数:${nums[@]}"
echo "最大的随机数:$max"
echo "最小的随机数:$min"
root@ubuntu2404-016:~/temp# /bin/bash compare_nums_for.sh
------------------------------
所有的随机数:22008 11878 19221 23650 28598 16951 21412 22666 25847 4770
最大的随机数:28598
最小的随机数:4770
6. 递归调用
使用递归调用,完成阶乘算法实现
root@ubuntu2404-016:~/temp# vim factorial_algorithm.sh
#!/bin/bash
# 功能:阶乘函数自调用实践
# 定义阶乘函数
self_func() {
# 接收一个参数
num=$1
if [ ${num} -eq 1 ]; then
echo 1
else
# 递归调用,并获取递减后的值
local temp=$(( ${num} - 1 ))
local result=$(self_func $temp)
# 输出结果:当前值乘以递归返回的值
echo $(( result * num ))
fi
}
# 检测逻辑效果
while true
do
read -p "请输入一个您要查询的阶乘:" value
result=$(self_func ${value})
echo "${value} 的阶乘是: ${result}"
done
# 执行脚本
root@ubuntu2404-016:~/temp# /bin/bash factorial_algorithm.sh
请输入一个您要查询的阶乘:5
5 的阶乘是: 120
请输入一个您要查询的阶乘:3
3 的阶乘是: 6
7. 鸡兔同笼
通过shell编程完成,30鸡和兔的头,80鸡和兔的脚,分别有几只鸡,几只兔?
root@ubuntu2404-016:~/temp# vim chicken_rabbit_cal.sh
#!/bin/bash
# 功能:鸡兔同笼问题实践
for i in $(seq 0 30); do
for j in $(seq 0 $((30 - $i))); do
if [ $(($i + $j)) -eq 30 ] && [ $((2 * $i + 4 * $j)) -eq 80 ]; then
echo "有$i只鸡,$j只兔";
exit
fi
done
done
# 执行脚本
root@ubuntu2404-016:~/temp# /bin/bash chicken_rabbit_cal.sh
有20只鸡,10只兔
root@ubuntu2404-016:~/temp#
8. 批量创建用户
结合编程的for循环,条件测试,条件组合,完成批量创建100个用户
1)for遍历1…100 2)先用id
命令判断是否存在 3)用户存在则说明存在,用户不存在则添加用户并说明已添加。
root@ubuntu2404-016:~/temp# vim create_user_batch.sh
#!/bin/bash
# 功能:批量创建100个用户
# 循环遍历 1 到 100
for i in {1..100}
do
# 生成用户名,例如 user1, user2, ..., user100
username="user$i"
# 判断用户是否存在
if id "$username" &>/dev/null; then
# 用户存在
echo "用户 $username 已经存在"
else
# 用户不存在,创建用户
sudo useradd "$username"
echo "用户 $username 已添加"
fi
done
# 执行脚本
root@ubuntu2404-016:~/temp# /bin/bash create_user_batch.sh
用户 user1 已经存在
用户 user2 已经存在
用户 user3 已添加
用户 user4 已添加
用户 user5 已添加
用户 user6 已添加
......
用户 user98 已添加
用户 user99 已添加
用户 user100 已添加
root@ubuntu2404-016:~/temp#
二、进程管理
1. 进程与线程的区别
**进程:**进程是程序的一次执行过程,它包含了程序的代码、数据和运行状态等信息。每个进程都有一个唯一的标
识符(PID),用于区分不同的进程。
线程:线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是CPU处理器调度和分派的基本
单位。一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。
2. 进程的结构
进程由程序、数据和进程控制块(Program Control Block,PCB)三部分组成。
程序是进程要执行的指令集合,
数据是进程在执行过程中需要处理的信息,
进程控制块则包含了进程的各种信息和控制信息,
如进程标识符(PID)、状态、优先级、程序计数器、寄存器集合等。
3. 进程状态
状态标识(STAT) | 含义说明 | 对应命令示例 | |
---|---|---|---|
R(Running) | 运行态:进程正在占用 CPU 或处于就绪队列中等待 CPU。 | `ps aux | grep R 可查看运行中的进程; top中STAT列显示 R`。 |
S(Sleeping) | 可中断睡眠态:进程因等待事件(如 I/O 完成)暂停,可被信号唤醒。 | ps aux 中多数进程为S (如后台服务);top 中显示s 。 | |
D(Disk Sleep) | 不可中断睡眠态:进程因等待关键 I/O(如磁盘读写)暂停,不可被信号唤醒。 | 通常与磁盘 I/O 相关,`ps aux | grep D` 可查看(较少见)。 |
T(Stopped) | 停止态:进程被暂停(如收到SIGSTOP 信号),需SIGCONT 信号恢复。 | kill -19 <PID> 可将进程转为T 态;ps aux 中显示T 。 | |
Z(Zombie) | 僵死态:进程已终止,但父进程未回收其资源(PCB 残留)。 | `ps aux | grep Z 可查看僵尸进程;需通过杀死父进程回收(如 kill -9 <父 PID>`)。 |
X(Dead) | 终止态:进程已完全结束,资源被回收(通常不显示)。 | 无直接显示,进程终止后自动进入此状态。 |
4.IPC通信和RPC通信实现的方式
- IPC 通信:进程间直接通过内核提供的机制(如管道、共享内存、信号量、消息队列、socket)实现本地通信。
- RPC 通信:通过网络协议(如 TCP/IP),将函数调用封装为网络消息,实现不同主机进程间的远程调用,需要客户端和服务端配合解析消息。
三、作业调度与状态转换
1. 前台与后台作业的区别
- 前台作业:通过终端启动,且启动后一直占据终端
- 后台作业:可通过终端启动,但启动后即转入后台运行(释放终端)
2. 状态转换方法
-
前台转后台:
- 执行前台作业时,按
Ctrl+Z
暂停作业,此时作业进入停止态(T
)。 - 输入
bg <作业号>
(如bg 1
)将其转为后台运行(状态变为Running
)。
示例:
# 前台运行ping root@ubuntu2404-016:~/temp# ping www.baidu.com PING www.a.shifen.com (183.2.172.177) 56(84) bytes of data. 64 bytes from 183.2.172.177: icmp_seq=1 ttl=128 time=39.0 ms 64 bytes from 183.2.172.177: icmp_seq=2 ttl=128 time=37.2 ms # 按Ctrl+Z暂停,显示[1]+ Stopped ping www.baidu.com ^Z [1]+ Stopped ping www.baidu.com root@ubuntu2404-016:~/temp# bg 1 # 转为后台运行,显示[1]+ ping www.baidu.com & [1]+ ping www.baidu.com & root@ubuntu2404-016:~/temp# 64 bytes from 183.2.172.177: icmp_seq=3 ttl=128 time=108 ms 64 bytes from 183.2.172.177: icmp_seq=4 ttl=128 time=63.4 ms
- 执行前台作业时,按
-
后台转前台:
-
输入
jobs
查看后台作业编号(如[1] Running ping ...
)。-
root@ubuntu2404-016:~/temp# jobs [1]+ Running ping www.baidu.com &
-
-
输入
fg <作业号>
(如fg 1
)将其转为前台运行。-
root@ubuntu2404-016:~/temp# fg 1 64 bytes from 183.2.172.177: icmp_seq=69 ttl=128 time=127 ms 64 bytes from 183.2.172.177: icmp_seq=70 ttl=128 time=94.0 ms
-
-
-
直接启动后台作业:
- 命令后加
&
,如ping www.baidu.com &
,直接在后台运行。
- 命令后加
-
停止后台作业:
- 输入
kill -19 <PID>
(SIGSTOP
信号)暂停后台作业。 - 输入
kill -18 <PID>
(SIGCONT
信号)恢复运行。
- 输入
四、计划任务
1. 每日凌晨1点,删除指定文件
-
创建测试文件:
mkdir -p /temp/testdir && touch /temp/testdir/toBeDeleted.txt
-
设置定时任务:
-
执行
crontab -e
编辑当前用户的定时任务(root 用户可添加系统级任务)。 -
添加以下内容(每日凌晨 1 点删除指定文件):
0 1 * * * rm -f /temp/testdir/toBeDeleted.txt
(格式:分 时 日 月 周 命令,
0 1 * * *
表示每日 1 点)
-
-
验证任务:
- 执行
crontab -l
查看任务是否添加成功。 - 手动触发测试:
rm -f /tmp/testdir/toBeDeleted.txt
,检查文件是否被删除。
- 执行
2. 每月月初对指定文件进行压缩
-
创建测试文件:
mkdir -p /data && echo "test data" > /data/source.txt
-
设置定时任务:
-
执行
crontab -e
添加以下内容(每月 1 日凌晨 0 点压缩文件,文件名含日期):
0 0 1 * * tar -zcvf /data/backup_$(date +\%Y\%m).tar.gz /data/source.txt
-
说明:
$(date +\%Y\%m)
生成年月(如 202507),\%
需转义(因 cron 中%
为特殊字符)。
-
-
验证:
- 执行
crontab -l
确认任务。 - 手动测试压缩命令:
tar -zcvf /data/backup_test.tar.gz /data/source.txt
,检查压缩包是否生成。
- 执行
五、进程管理工具
1. top
:实时监控进程资源占用
-
用途:查看 CPU、内存使用率,进程状态等。
-
常用操作
top # 启动实时监控 top -d 5 # 每5秒刷新一次 top -u root # 仅显示root用户的进程
-
交互命令:
P
:按 CPU 使用率排序;M
:按内存使用率排序;k
:杀死指定进程(输入 PID 和信号 9)。
2. htop
:增强版top
-
用途:更直观的彩色界面,支持鼠标操作。
-
常用操作
htop # 启动,默认显示CPU、内存、进程列表 htop -d 3 # 每3秒刷新一次
-
交互命令:
F6
:选择排序字段;F9
:发送信号(如 9 杀死进程);q
:退出。
3. iotop
:监控磁盘 I/O 使用
-
用途:查看进程的磁盘读写速率。
-
常用操作
iotop # 启动,显示实时I/O情况 iotop -o # 仅显示正在产生I/O的进程 iotop -p 1234 # 仅监控PID为1234的进程
4. iostat
:监控 CPU 和磁盘统计
-
用途:查看 CPU 使用率、磁盘 I/O 吞吐量。
-
常用操作
iostat # 显示CPU和磁盘概览 iostat -d 2 3 # 每2秒刷新一次,共3次(仅显示磁盘) iostat -x sda # 显示sda磁盘的详细I/O统计(如tps、读写速率)
5. free
:查看内存使用情况
-
用途:显示物理内存、交换分区使用情况。
-
常用操作
free -h # 人类可读格式(如Gi、Mi) free -m # 以MB为单位显示
6. vmstat
:监控系统整体性能
-
用途:查看进程、内存、磁盘 I/O、CPU 等汇总信息。
-
常用操作
vmstat # 显示一次统计 vmstat 1 5 # 每1秒刷新一次,共5次
- 关键指标:
r
(运行队列进程数)、si/so
(交换分区读写速率)、us/sy
(用户 / 内核 CPU 占比)。
- 关键指标:
六、Linux系统启动流程
首先开机通电,BIOS 会先做硬件自检(POST),看看 CPU、内存这些有没有问题。
接着 BIOS/UEFI 根据设置的启动顺序找启动设备,比如硬盘,然后加载 GRUB 这类 Bootloader。
GRUB 会显示启动菜单,选好系统后,就加载内核文件(vmlinuz)和临时根文件系统(initramfs)。
内核启动后,会检测硬件、装驱动,先只读挂载根文件系统,然后启动第一个用户进程 —— 老系统(比如 CentOS 6)是/sbin/init,新系统(比如 Rocky 9、Ubuntu)是systemd。
最后systemd会启动各种服务(比如网络、登录服务),切换到真正的根文件系统,完成后到登录界面。
七、内核设计流派及特点
- 单内核:追求高性能,把所有核心功能(比如调度、内存管理)都塞到内核里,像 Linux、BSD 就是这样。优点是快、简单,缺点是改点东西得重编内核,一出问题整个系统容易崩。
- 微内核:看重稳定和安全,只留最基础的功能(比如进程调度)在内核,其他功能(比如文件系统)放用户空间当服务跑。好处是某个服务挂了不影响内核,坏处是模块间通信麻烦,性能差一些。
- 混合内核:平衡性能和灵活,核心功能放内核,非核心的(比如驱动)可动态选地方跑,Windows 属于这一种。兼顾了速度和扩展性,但设计起来复杂,不好调试。
- 外核:非常灵活,内核只提供最底层硬件接口,其他全让应用自己实现。理论上很灵活,但开发困难。
八、systemd 服务配置文件
systemd 的服务配置文件一般分为三部分:
- [Unit]:描述服务是什么,依赖哪些其他服务,例如
After=network.service
表示等网络起来再启动它。 - [Service]:核心部分,写启动命令(
ExecStart
)、停止命令(ExecStop
),还有进程类型(Type
)等。例如 nginx 的配置里有启动前检查配置的ExecStartPre
。 - [Install]:设定开机启动相关的,例如
WantedBy=multi-user.target
表示在多用户模式下启动。
修改配置后,使用用systemctl daemon-reload
使其生效。
九、awk实践
1. 基础操作实践
提取列内容
# 创建测试文件 awk.txt
root@ubuntu2404-016:/temp/testdir# vim awk.txt
# 输入:
# nihao awk1 awk2 awk3
# nihao awk4 awk5 awk6
# nihao awk7 awk8 awk9
# 提取第 1 列
root@ubuntu2404-016:/temp/testdir# awk '{print $1}' awk.txt
nihao
nihao
nihao
root@ubuntu2404-016:/temp/testdir#
# 提取第 3 列和最后一列
root@ubuntu2404-016:/temp/testdir# awk '{print $3, $NF}' awk.txt
awk2 awk3
awk5 awk6
awk8 awk9
root@ubuntu2404-016:/temp/testdir#
指定分隔符
# 处理 /etc/passwd(以 : 为分隔符)
root@ubuntu2404-016:/temp/testdir# head -n1 /etc/passwd | awk -F ':' '{print "用户名:"$1, "shell类型:"$7}'
用户名:root shell类型:/bin/bash
root@ubuntu2404-016:/temp/testdir#
# 用 -v 定义 FS(与 -F 功能一致,-F 优先级更高)
root@ubuntu2404-016:/temp/testdir# head -n1 /etc/passwd | awk -v FS=":" '{print $1, $7}'
root /bin/bash
root@ubuntu2404-016:/temp/testdir#
行号与内容结合
# 打印行号和整行内容
awk '{print NR, $0}' awk.txt
root@ubuntu2404-016:/temp/testdir# awk '{print NR, $0}' awk.txt
1 nihao awk1 awk2 awk3
2 nihao awk4 awk5 awk6
3 nihao awk7 awk8 awk9
root@ubuntu2404-016:/temp/testdir#
# 只打印第 2 行的第 1 列和第 3 列
root@ubuntu2404-016:/temp/testdir# awk 'NR==2 {print NR, $1, $3}' awk.txt
2 nihao awk5
root@ubuntu2404-016:/temp/testdir#
2. 格式化输出
print
与 printf
print
:自动换行,默认以OFS
分隔字段。printf
:需手动指定换行(\n
),支持格式化字符(如%s
字符串、%d
整数)。
# print 示例(自定义输出分隔符)
root@ubuntu2404-016:/temp/testdir# awk -F ':' 'BEGIN{OFS="~~~"} {print $1, $7}' /etc/passwd | head -n1
root~~~/bin/bash
root@ubuntu2404-016:/temp/testdir#
# printf 示例(格式化对齐)
root@ubuntu2404-016:/temp/testdir# awk '{printf "行号:%d, 第一列:%-8s, 第三列:%s\n", NR, $1, $3}' awk.txt
行号:1, 第一列:nihao , 第三列:awk2
行号:2, 第一列:nihao , 第三列:awk5
行号:3, 第一列:nihao , 第三列:awk8
root@ubuntu2404-016:/temp/testdir#
3. 变量与运算实践
内置变量扩展
# 统计 /etc/passwd 的行数(END 中使用 NR)
root@ubuntu2404-016:/temp/testdir# awk 'END{print "总行数:", NR}' /etc/passwd
总行数: 135
root@ubuntu2404-016:/temp/testdir#
# 多文件处理时用 FNR 和 FILENAME
root@ubuntu2404-016:/temp/testdir# awk '{print FNR, FILENAME, $0}' /etc/issue /etc/debian_version
1 /etc/issue Ubuntu 24.04.2 LTS \n \l
2 /etc/issue
1 /etc/debian_version trixie/sid
root@ubuntu2404-016:/temp/testdir#
自定义变量
# 用 -v 定义变量
root@ubuntu2404-016:/temp/testdir# awk -v name="chen" 'BEGIN{print "姓名:", name}'
姓名: chen
root@ubuntu2404-016:/temp/testdir#
# 脚本内定义变量(统计 /etc/passwd 中 UID>=1000 的用户)
root@ubuntu2404-016:/temp/testdir# awk -F ':' 'BEGIN{count=0} $3>=1000 {count++; print $1} END{print "总数:", count}' /etc/passwd
chen
test1
test2
test7
总数: 4
root@ubuntu2404-016:/temp/testdir#
4. 运算实践
数学运算
# 基础运算
root@ubuntu2404-016:/temp/testdir# awk 'BEGIN{print 100+3, 100*3, 100%3}'
103 300 1
root@ubuntu2404-016:/temp/testdir#
# 统计学生总分(course_scores.txt 内容:姓名 语文 数学 历史)
vim course_scores.txt
姓名 语文 数学 历史
张三 89 91 86
李四 92 90 88
root@ubuntu2404-016:/temp/testdir# awk 'NR>=2 {total=$2+$3+$4; print $1"总分:", total}' course_scores.txt
张三总分: 266
李四总分: 270
root@ubuntu2404-016:/temp/testdir#
逻辑运算(&& 与、|| 或、! 非)
# 筛选 /etc/passwd 中 UID=0 或 UID>=1000 的用户
root@ubuntu2404-016:/temp/testdir# awk -F ':' '$3==0 || $3>=1000 {print $1, $3}' /etc/passwd
root 0
nobody 65534
# 筛选 UID 不小于 1000 的用户(! 取反)
root@ubuntu2404-016:/temp/testdir# awk -F ':' '!($3<1000) {print $1, $3}' /etc/passwd
nobody 65534
root@ubuntu2404-016:/temp/testdir#
匹配运算(~ 包含、!~ 不包含)
# 筛选 /etc/passwd 中用户名以 "ro" 开头的行
root@C20250701187335:~# awk -F ':' '$1 ~ "^ro" {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
root@C20250701187335:~#
# 筛选用户名不含 "ftp" 的行
root@C20250701187335:~# awk -F ':' '$1 !~ "ftp" {print $1}' /etc/passwd | head -n3
root
daemon
bin
root@C20250701187335:~#
5. 流程控制实践
if 条件判断
# 判断学生成绩是否优秀(总分>=240)
root@ubuntu2404-016:/temp/testdir# awk 'NR>=2 {
> total=$2+$3+$4;
> if(total>=240) {
> print $1" 优秀("total"分)"
> } else {
> print $1" 良好("total"分)"
> }
> }' course_scores.txt
张三 优秀(266分)
李四 优秀(270分)
循环(for/while)
# for 循环计算 1~100 的和
root@ubuntu2404-016:/temp/testdir# awk 'BEGIN{sum=0; for(i=1;i<=100;i++) sum+=i; print "总和:", sum}'
总和: 5050
root@ubuntu2404-016:/temp/testdir#
# while 循环求最大值(num.txt 内容:0 234 252 3246 2245)
root@ubuntu2404-016:/temp/testdir# vim num.txt
0 32 65 15 73 52
root@ubuntu2404-016:/temp/testdir# awk '{
> max=$1; i=1;
> while(i<=NF) {
> if($i>max) max=$i;
> i++
> };
> print "最大值:", max
> }' num.txt
最大值: 73
root@ubuntu2404-016:/temp/testdir#
流程控制(continue/next)
# continue:求 1~100 的奇数和
root@ubuntu2404-016:/temp/testdir# awk 'BEGIN{sum=0; for(i=1;i<=100;i++) {if(i%2==0) continue; sum+=i}; print sum}'
2500
root@ubuntu2404-016:/temp/testdir#
# next:跳过第 2 行,打印其他行
root@ubuntu2404-016:/temp/testdir# awk 'NR==2 {next} {print}' awk.txt
nihao awk1 awk2 awk3
nihao awk7 awk8 awk9
root@ubuntu2404-016:/temp/testdir#
6. 数组实践
数组定义与遍历
# 定义数组并遍历
root@ubuntu2404-016:/temp/testdir# awk 'BEGIN{
> array["yuwen"]=78;
> array["shuxue"]=89;
> for(i in array) print i":", array[i]
> }'
yuwen: 78
shuxue: 89
root@ubuntu2404-016:/temp/testdir#
数组去重
# 去重 array.txt
root@ubuntu2404-016:/temp/testdir# vim array.txt
a
b
a
cc
root@ubuntu2404-016:/temp/testdir# awk '!array[$0]++' array.txt
a
b
cc
root@ubuntu2404-016:/temp/testdir#
# 原理:首次出现时 array[$0] 为 0,!0 为真,打印;后续 ++ 使值>0,!为假不打印。
统计功能
# 统计 nginx 访问日志中每个 IP 的访问次数
root@C20250701187335:/docker/nginx/logs# awk -F '"' '{ip[$(NF-1)]++} END{for(i in ip) print i, ip[i]}' access.log
192.168.48.244 1
- 24729
192.168.253.128 1
10.0.232.176 1
1.1.146.236 1
7. 综合实践案例
学生成绩统计
root@ubuntu2404-016:/temp/testdir# vim course_scores.txt
姓名 语文 数学 历史
张三 89 91 86
李四 92 90 88
王五 78 68 65
# 统计每个学生总分、各科平均分及总人数
root@ubuntu2404-016:/temp/testdir# awk '
> BEGIN{
> printf "|姓名|语文|数学|历史|总分|\n"
> printf "|----|----|----|----|----|\n"
> yu=0; shu=0; li=0; total=0
> }
> NR>=2{
> yu+=$2; shu+=$3; li+=$4;
> sum=$2+$3+$4; total+=sum;
> printf "|%s|%d|%d|%d|%d|\n", $1, $2, $3, $4, sum
> }
> END{
> printf "|----|----|----|----|----|\n"
> printf "|平均分|%.1f|%.1f|%.1f|%.1f|\n", yu/(NR-1), shu/(NR-1), li/(NR-1), total/(NR-1)
> printf "|总人数|%d|\n", NR-1
> }' course_scores.txt
|姓名|语文|数学|历史|总分|
|----|----|----|----|----|
|张三|89|91|86|266|
|李四|92|90|88|270|
|王五|78|68|65|211|
|----|----|----|----|----|
|平均分|86.3|83.0|79.7|249.0|
|总人数|3|
root@ubuntu2404-016:/temp/testdir#
十、DNS
1. DNS 域名三级结构、工作原理及查询方式
- 域名三级结构:从右到左分三级,最右是顶级域(如.com、.cn),中间是二级域(如baidu.com中的 baidu),最左是子域名 / 主机名(如www.baidu,com中的 www)。
- 工作原理:用户输域名后,先查本地 hosts 和缓存;没结果就找本地 DNS 服务器。本地 DNS 若没缓存,会逐层查根服务器→顶级域服务器→二级域服务器,最终拿到 IP 返回给用户。
- 查询方式:
- 递归查询:客户端问本地 DNS,本地 DNS “包办” 所有查询,直到返回结果(客户端→本地 DNS )。
- 迭代查询:本地 DNS 查其他服务器时,对方只给下一个服务器地址,本地 DNS 自己再去查,直到拿到结果(本地 DNS→根→顶级域→二级域,逐步推进)。
2. 实现私有 DNS 供本地网络递归查询(基于Ubuntu24.04)
(1)安装BIND软件
sudo apt update
sudo apt install bind9 bind9utils bind9-doc -y
(2)配置BIND主配置文件
vim /etc/bind/named.conf.options
options {
directory "/var/cache/bind";
// 监听本地IP地址
listen-on { 10.0.0.16; };
// 允许查询的本地网络
allow-query { 10.0.0.0/24; };
// 允许递归查询
recursion yes;
// 设置转发器
forwarders {
8.8.8.8;
};
// If there is a firewall between you and nameservers you want
// to talk to, you may need to fix the firewall to allow multiple
// ports to talk. See http://www.kb.cert.org/vuls/id/800113
// If your ISP provided one or more IP addresses for stable
// nameservers, you probably want to use them as forwarders.
// Uncomment the following block, and insert the addresses replacing
// the all-0's placeholder.
// forwarders {
// 0.0.0.0;
// };
//========================================================================
// If BIND logs error messages about the root key being expired,
// you will need to update your keys. See https://www.isc.org/bind-keys
//========================================================================
//dnssec-validation auto;
//listen-on-v6 { any; };
};
(3)配置本地域名解析
vim /etc/bind/named.conf.local
# 本地域名设置为local.lan
zone "local.lan" {
type master;
file "/etc/bind/db.local.lan";
};
zone "0.0.10.in-addr.arpa" {
type master;
file "/etc/bind/db.10.0.0";
};
(4)创建区域文件
# 创建正向解析文件
cp /etc/bind/db.local /etc/bind/db.local.lan
vim /etc/bind/db.local.lan
$TTL 604800
@ IN SOA localhost. root.localhost. (
2 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS ns.local.lan.
ns IN A 10.0.0.16
gateway IN A 10.0.0.2
# 创建反向解析文件
cp /etc/bind/db.127 /etc/bind/db.10.0.0
vim /etc/bind/db.10.0.0
$TTL 604800
@ IN SOA localhost. root.localhost. (
1 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS ns.local.lan.
16 IN PTR ns.local.lan.
2 IN PTR gateway.local.lan.
(5)检测配置文件语法
# 检查主配置文件语法
root@ubuntu2404-016:/temp/testdir# named-checkconf
# 检查区域文件
root@ubuntu2404-016:/temp/testdir# named-checkzone local.lan /etc/bind/db.local.lan
zone local.lan/IN: loaded serial 2
OK
root@ubuntu2404-016:/temp/testdir# named-checkzone 0.0.10.in-addr.arpa /etc/bind/db.10.0.0
zone 0.0.10.in-addr.arpa/IN: loaded serial 1
OK
root@ubuntu2404-016:/temp/testdir#
(6)重启BIND服务
# 重启服务
systemctl restart bind9
# 设置开机自启
systemctl enable named.service
Synchronizing state of named.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
Executing: /usr/lib/systemd/systemd-sysv-install enable named
(7)验证DNS服务器功能
root@ubuntu2404-016:/temp/testdir# systemctl status bind9
● named.service - BIND Domain Name Server
Loaded: loaded (/usr/lib/systemd/system/named.service; enabled; pr>
Active: active (running) since Tue 2025-07-22 03:16:28 UTC; 8min a>
Docs: man:named(8)
......
# 测试互联网域名解析
root@ubuntu2404-016:/temp/testdir# dig @10.0.0.16 google.com
; <<>> DiG 9.18.30-0ubuntu0.24.04.2-Ubuntu <<>> @10.0.0.16 google.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 55664
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: 6bcc32b521299a8a01000000687f04f43bcadc2ce5d11e22 (good)
;; QUESTION SECTION:
;google.com. IN A
;; ANSWER SECTION:
google.com. 60 IN A 59.24.3.174
;; Query time: 925 msec
;; SERVER: 10.0.0.16#53(10.0.0.16) (UDP)
;; WHEN: Tue Jul 22 03:26:44 UTC 2025
;; MSG SIZE rcvd: 83
root@ubuntu2404-016:/temp/testdir#
# 测试本地域名解析
root@ubuntu2404-016:/temp/testdir# dig @10.0.0.16 ns.local.lan
; <<>> DiG 9.18.30-0ubuntu0.24.04.2-Ubuntu <<>> @10.0.0.16 ns.local.lan
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 3578
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; COOKIE: ddfda8d70dceda5b01000000687f051c8bacf73cd07b5636 (good)
;; QUESTION SECTION:
;ns.local.lan. IN A
;; ANSWER SECTION:
ns.local.lan. 604800 IN A 10.0.0.16
;; Query time: 0 msec
;; SERVER: 10.0.0.16#53(10.0.0.16) (UDP)
;; WHEN: Tue Jul 22 03:27:24 UTC 2025
;; MSG SIZE rcvd: 85
root@ubuntu2404-016:/temp/testdir#
(8)在客户端使用私有DNS
# 查看网络配置文件
root@ubuntu2404-016:/temp/testdir# ls /etc/netplan/
50-cloud-init.yaml
root@ubuntu2404-016:/temp/testdir#
# 编辑配置文件
root@ubuntu2404-016:/temp/testdir# vim /etc/netplan/50-cloud-init.yaml
network:
version: 2
ethernets:
ens33:
addresses:
- "10.0.0.16/24"
nameservers:
addresses: [10.0.0.16]
# 应用配置
netplan apply
# 查看当前 DNS 服务器配置
root@ubuntu2404-016:/temp/testdir# resolvectl status
Global
Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Link 2 (ens33)
Current Scopes: DNS
Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS
DNSSEC=no/unsupported
Current DNS Server: 10.0.0.16
DNS Servers: 10.0.0.16
root@ubuntu2404-016:/temp/testdir#
3. DNS 服务器类型、解析答案、正反解析及资源记录
- 服务器类型:根服务器(顶级域入口)、顶级域服务器(管理.com/.cn 等)、权威服务器(管理具体域名,分主从)、本地递归服务器(帮客户端查域名)。
- 解析答案:
- 权威应答(aa 标志):来自管理该域名的服务器,结果最权威。
- 非权威应答:来自缓存,不是直接从权威服务器拿的。
- 正反解析域:
- 正向解析域:存域名→IP 的映射(如www.baidu.com→1.2.3.4)。
- 反向解析域:存 IP→域名的映射(如 1.2.3.4→www.baidu.com),区域名是 IP 反转加
.in-addr.arpa
(如 1.2.3.4 对应 4.3.2.1.in-addr.arpa)。
- 资源记录:每条记录含域名(Owner)、缓存时间(TTL)、类型(CLASS,通常 IN)、记录类型(TYPE)、具体数据(RDATA)。常见类型:A(IPv4)、AAAA(IPv6)、CNAME(别名)、NS(域名服务器)、MX(邮件服务器)、PTR(反向解析)、SOA(起始授权记录,域的 “总管” 信息)。