运维学习第五周作业:Shell、进程、服务、内核、DNS

一、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"]=valuearr_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 auxgrep R 可查看运行中的进程;top中STAT列显示R`。
S(Sleeping)可中断睡眠态:进程因等待事件(如 I/O 完成)暂停,可被信号唤醒。ps aux中多数进程为S(如后台服务);top中显示s
D(Disk Sleep)不可中断睡眠态:进程因等待关键 I/O(如磁盘读写)暂停,不可被信号唤醒。通常与磁盘 I/O 相关,`ps auxgrep D` 可查看(较少见)。
T(Stopped)停止态:进程被暂停(如收到SIGSTOP信号),需SIGCONT信号恢复。kill -19 <PID>可将进程转为T态;ps aux中显示T
Z(Zombie)僵死态:进程已终止,但父进程未回收其资源(PCB 残留)。`ps auxgrep Z 可查看僵尸进程;需通过杀死父进程回收(如kill -9 <父 PID>`)。
X(Dead)终止态:进程已完全结束,资源被回收(通常不显示)。无直接显示,进程终止后自动进入此状态。

4.IPC通信和RPC通信实现的方式

  • IPC 通信:进程间直接通过内核提供的机制(如管道、共享内存、信号量、消息队列、socket)实现本地通信。
  • RPC 通信:通过网络协议(如 TCP/IP),将函数调用封装为网络消息,实现不同主机进程间的远程调用,需要客户端和服务端配合解析消息。

三、作业调度与状态转换

1. 前台与后台作业的区别

  • 前台作业:通过终端启动,且启动后一直占据终端
  • 后台作业:可通过终端启动,但启动后即转入后台运行(释放终端)

2. 状态转换方法

  1. 前台转后台

    • 执行前台作业时,按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
    
  2. 后台转前台

    • 输入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
        
  3. 直接启动后台作业

    • 命令后加&,如ping www.baidu.com &,直接在后台运行。
  4. 停止后台作业

    • 输入kill -19 <PID>SIGSTOP信号)暂停后台作业。
    • 输入kill -18 <PID>SIGCONT信号)恢复运行。

四、计划任务

1. 每日凌晨1点,删除指定文件

  1. 创建测试文件

    mkdir -p /temp/testdir && touch /temp/testdir/toBeDeleted.txt
    
  2. 设置定时任务

    • 执行crontab -e编辑当前用户的定时任务(root 用户可添加系统级任务)。

    • 添加以下内容(每日凌晨 1 点删除指定文件):

      0 1 * * * rm -f /temp/testdir/toBeDeleted.txt
      

      (格式:分 时 日 月 周 命令,0 1 * * *表示每日 1 点)

  3. 验证任务

    • 执行crontab -l查看任务是否添加成功。
    • 手动触发测试:rm -f /tmp/testdir/toBeDeleted.txt,检查文件是否被删除。

2. 每月月初对指定文件进行压缩

  1. 创建测试文件

    mkdir -p /data && echo "test data" > /data/source.txt
    
  2. 设置定时任务

    • 执行crontab -e

      添加以下内容(每月 1 日凌晨 0 点压缩文件,文件名含日期):

      0 0 1 * * tar -zcvf /data/backup_$(date +\%Y\%m).tar.gz /data/source.txt
      
    • 说明:$(date +\%Y\%m)生成年月(如 202507),\%需转义(因 cron 中%为特殊字符)。

  3. 验证

    • 执行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. 格式化输出

printprintf

  • 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(起始授权记录,域的 “总管” 信息)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值