Shell循环正则sed与awk

瑞克 0 阅读 linux服务

Shell循环、正则表达式、sed与awk

写在前面:如果说变量是给数据"贴标签",那循环就是让计算机当"流水线工人"——重复干活直到完工。正则表达式则是文本世界的"放大镜+筛选器",而sed和awk是两位编辑大师:sed像一把锋利的剪刀,awk则是一台精密的分析仪。本章我们将用大量实战示例,把这些工具彻底拿下!


1 for循环——让重复的事自动跑

生活类比

想象你是学校食堂的打饭阿姨,面前排了30个学生,你的动作永远是:伸手→打饭→递出去。for循环就是把这个重复动作写成一行命令。

问题场景

运维经常需要对一批服务器执行相同操作,比如测试10台主机的连通性。手动ping 10次?太累了。

1.1 列表遍历

#!/bin/bash
# 遍历一组服务器IP,逐一检测连通性
servers="192.168.1.1 192.168.1.2 192.168.1.3 192.168.1.10"

for ip in $servers; do
    if ping -c 1 -W 1 "$ip" &>/dev/null; then
        echo "[OK]   $ip 在线"
    else
        echo "[FAIL] $ip 离线"
    fi
done

输出示例:

[OK]   192.168.1.1 在线
[FAIL] 192.168.1.2 离线
[OK]   192.168.1.3 在线
[FAIL] 192.168.1.10 离线

1.2 花括号range——生成序列

# 批量创建用户user01~user10
for i in {01..10}; do
    username="student${i}"
    useradd "$username" 2>/dev/null
    echo "Created: $username"
done

# 批量生成序号文件
for n in {1..5}; do
    touch "report_${n}.txt"
done
ls report_*.txt

1.3 C风格for循环

# 计算1到100的和
sum=0
for ((i=1; i<=100; i++)); do
    sum=$((sum + i))
done
echo "1到100的和 = $sum"
# 输出:1到100的和 = 5050

# 打印九九乘法表(节选前3行演示)
for ((i=1; i<=9; i++)); do
    for ((j=1; j<=i; j++)); do
        printf "%dx%d=%-4d" $j $i $((i*j))
    done
    echo
done

互动提问:如果要打印1到50中所有的偶数,用哪种for循环写法最简洁?

点击查看答案 `for i in {2..50..2}; do echo $i; done` —— 花括号支持步长!

2 while循环——条件满足就一直干

生活类比

while循环像洗衣机:只要还有衣服在桶里(条件为真),它就一直在转。

2.1 计数器控制

#!/bin/bash
# 监控磁盘使用率,超过80%就报警
count=0
while [ $count -lt 5 ]; do
    usage=$(df -h / | awk 'NR==2{print $5}' | tr -d '%')
    echo "第$((count+1))次检测:根分区使用率 ${usage}%"
    if [ "$usage" -gt 80 ]; then
        echo "  *** 警告:磁盘使用率超过80%!***"
    fi
    count=$((count + 1))
    sleep 2
done

2.2 逐行读取文件

# 读取IP列表文件,批量ping
# 假设 iplist.txt 内容如下:
# 192.168.1.1
# 192.168.1.2
# 10.0.0.1

while read -r ip; do
    # 跳过空行和注释
    [[ -z "$ip" || "$ip" == \#* ]] && continue
    if ping -c 1 -W 1 "$ip" &>/dev/null; then
        echo "$ip => 可达"
    else
        echo "$ip => 不可达"
    fi
done < /root/iplist.txt

实战技巧:读取CSV格式的用户信息文件

# users.csv:
# zhangsan,20,北京
# lisi,22,上海
# wangwu,19,广州

while IFS=',' read -r name age city; do
    echo "姓名: $name, 年龄: $age, 城市: $city"
done < users.csv

3 until循环——"不到黄河心不死"

until和while相反:条件为假时执行,条件为真时退出

#!/bin/bash
# 等待某个服务启动成功
until curl -s http://localhost:8080/health &>/dev/null; do
    echo "服务尚未就绪,3秒后重试..."
    sleep 3
done
echo "服务已启动,开始工作!"

互动提问:while和until能互相替代吗?

点击查看答案 可以!`until condition` 等价于 `while ! condition`。选择哪个取决于哪种写法更易读。

4 break与continue——循环的"遥控器"

命令作用生活类比
break立即跳出整个循环跑步跑到一半腿抽筋,直接退赛
continue跳过本次,进入下一次循环吃葡萄吃到一颗坏的,吐掉继续吃下一颗
#!/bin/bash
# 查找第一个可ping通的IP就停止(break)
for ip in 192.168.1.{1..254}; do
    if ping -c 1 -W 1 "$ip" &>/dev/null; then
        echo "找到第一台在线主机: $ip"
        break
    fi
done

echo "---"

# 只处理偶数编号(continue跳过奇数)
for i in {1..10}; do
    if [ $((i % 2)) -ne 0 ]; then
        continue   # 奇数跳过
    fi
    echo "处理偶数: $i"
done

5 case分支——多路选择器

生活类比

case就像自动售货机:你按A1出可乐,按B2出雪碧,按错了提示"选择无效"。

实战:服务控制脚本

#!/bin/bash
# myservice.sh —— 模拟Nginx服务控制
# 用法: ./myservice.sh {start|stop|restart|status}

SERVICE="nginx"

case "$1" in
    start)
        echo "正在启动 $SERVICE ..."
        systemctl start "$SERVICE" 2>/dev/null || echo "启动命令已发送"
        echo "$SERVICE 已启动。"
        ;;
    stop)
        echo "正在停止 $SERVICE ..."
        systemctl stop "$SERVICE" 2>/dev/null || echo "停止命令已发送"
        echo "$SERVICE 已停止。"
        ;;
    restart)
        echo "正在重启 $SERVICE ..."
        systemctl restart "$SERVICE" 2>/dev/null || echo "重启命令已发送"
        echo "$SERVICE 已重启。"
        ;;
    status)
        echo "检查 $SERVICE 状态..."
        systemctl is-active "$SERVICE" 2>/dev/null || echo "状态查询已执行"
        ;;
    *)
        echo "用法: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac

case语法要点:

case 变量 in
    模式1)
        命令...
        ;;           # 相当于break
    模式2|模式3)     # 多模式匹配(或的关系)
        命令...
        ;;
    *)               # 通配,相当于default
        命令...
        ;;
esac

互动提问$1 是什么?为什么脚本里用 $1 而不是 $name

点击查看答案 `$1` 是位置参数,代表脚本的第一个参数。运行 `./myservice.sh start` 时,`$1` 的值就是 `start`。这是Shell内置的参数传递机制。

6 函数——给一组命令取个名字

生活类比

函数就像菜谱:你把"西红柿炒鸡蛋"的步骤写下来,以后喊一声"做西红柿炒蛋"就行,不用每次重新想步骤。

6.1 基本定义与调用

#!/bin/bash

# 定义函数(两种写法等价)
check_disk() {
    local mount_point=$1          # local声明局部变量
    local threshold=${2:-80}      # 默认阈值80%
    local usage

    usage=$(df -h "$mount_point" | awk 'NR==2{print $5}' | tr -d '%')

    if [ "$usage" -gt "$threshold" ]; then
        echo "[警告] $mount_point 使用率 ${usage}% 超过阈值 ${threshold}%"
        return 1                  # 返回状态码(0=成功,非0=失败)
    else
        echo "[正常] $mount_point 使用率 ${usage}%"
        return 0
    fi
}

# 调用函数
check_disk "/"
check_disk "/boot" 90

# 获取函数返回值
check_disk "/" 85
echo "函数退出码: $?"

6.2 函数参数与返回值

#!/bin/bash

# Shell函数只能通过echo或return(状态码)返回结果
# 返回字符串用echo,返回成功/失败用return

get_file_count() {
    local dir=$1
    local count
    count=$(find "$dir" -type f 2>/dev/null | wc -l)
    echo "$count"    # 通过echo"返回"结果
}

# 用$()捕获函数的echo输出
num=$(get_file_count "/etc")
echo "/etc目录下有 $num 个文件"

# 传递多个参数
show_info() {
    echo "函数名: $0"       # 脚本名
    echo "第1个参数: $1"
    echo "第2个参数: $2"
    echo "参数总数: $#"
    echo "所有参数: $*"
}

show_info "hello" "world" "linux"

6.3 local变量——防止"串门"

#!/bin/bash

name="全局变量张三"

greet() {
    local name="局部变量李四"   # 只在函数内有效
    echo "函数内: $name"
}

greet                # 输出:函数内: 局部变量李四
echo "函数外: $name" # 输出:函数外: 全局变量张三

互动提问:如果去掉 local 关键字,函数外打印的 name 会变成什么?

点击查看答案 会变成"局部变量李四"!因为没有local,函数内的赋值会覆盖全局变量。这就是local的重要性——隔离作用域。

7 变量替换——Shell的"瑞士军刀"

这些语法看似花哨,实则每天都在用。

语法含义生活类比
${var:-default}var为空时返回default银行卡没钱?用默认零花钱
${var:+alt}var非空时返回alt有VIP卡?享受替代优惠
${#var}返回var的长度量一下绳子有多长
${var//old/new}全局替换把文章里所有"他"换成"她"
${var%pattern}从末尾删除最短匹配剪掉尾巴
${var%%pattern}从末尾删除最长匹配大刀砍尾巴
${var#pattern}从开头删除最短匹配削掉头部
${var##pattern}从开头删除最长匹配大刀砍头部

实战示例

#!/bin/bash

# === ${var:-default} 设定默认值 ===
echo "请输入备份目录(回车使用默认/tmp/backup):"
read -r backup_dir
backup_dir=${backup_dir:-/tmp/backup}
echo "将使用目录: $backup_dir"

# === ${var:+alt} 条件替代 ===
DEBUG=""
echo ${DEBUG:+"调试模式已开启"}   # DEBUG为空,什么都不输出
DEBUG="yes"
echo ${DEBUG:+"调试模式已开启"}   # 输出:调试模式已开启

# === ${#var} 获取字符串长度 ===
filename="nginx_access_2024.log"
echo "文件名长度: ${#filename}"   # 输出:22

# === ${var//old/new} 全局替换 ===
logline="ERROR: disk full, ERROR: memory low, INFO: ok"
echo "${logline//ERROR/警告}"
# 输出:警告: disk full, 警告: memory low, INFO: ok

# === 提取文件名和扩展名 ===
filepath="/var/log/nginx/access.log"
echo "完整路径: $filepath"
echo "文件名:   ${filepath##*/}"   # access.log
echo "目录名:   ${filepath%/*}"    # /var/log/nginx
echo "扩展名:   ${filepath##*.}"   # log
echo "无扩展名: ${filepath%.*}"    # /var/log/nginx/access

8 数组——一次管理一群数据

8.1 普通数组(索引数组)

#!/bin/bash

# 定义数组(三种方式)
fruits=("apple" "banana" "cherry" "durian")
ports[0]=80
ports[1]=443
ports[2]=8080

# 访问元素
echo "第一个水果: ${fruits[0]}"
echo "所有水果:   ${fruits[@]}"
echo "水果数量:   ${#fruits[@]}"
echo "第二个端口: ${ports[1]}"

# 遍历数组
echo "=== 遍历所有端口 ==="
for port in "${ports[@]}"; do
    echo "检查端口: $port"
done

# 数组切片
echo "取第2~3个水果: ${fruits[@]:1:2}"  # banana cherry

# 添加元素
fruits+=("elderberry")
echo "添加后: ${fruits[@]}"

# 删除元素
unset fruits[2]    # 删除cherry
echo "删除后: ${fruits[@]}"

8.2 关联数组(字典/哈希表)——Bash 4.0+

#!/bin/bash

# 必须先声明
declare -A service_ports

service_ports=(
    ["nginx"]=80
    ["sshd"]=22
    ["mysql"]=3306
    ["redis"]=6379
)

# 访问
echo "Nginx端口: ${service_ports[nginx]}"

# 遍历
echo "=== 所有服务端口 ==="
for svc in "${!service_ports[@]}"; do
    echo "$svc => ${service_ports[$svc]}"
done

# 获取所有键和值
echo "所有服务: ${!service_ports[@]}"
echo "所有端口: ${service_ports[@]}"

互动提问:关联数组和普通数组的最大区别是什么?

点击查看答案 普通数组用数字下标(0, 1, 2...),关联数组用字符串键名(如 "nginx", "mysql")。关联数组更像Python的字典或Java的HashMap。

9 正则表达式——文本的"超级搜索术"

生活类比

普通搜索像在图书馆找"三体"这本书,正则搜索像在图书馆找"所有刘慈欣写的、2010年后出版的、名字带'星'的书"。

9.1 BRE(基础正则)与ERE(扩展正则)

元字符BREERE含义示例
...任意单个字符a.c → abc, axc
***前一个字符0次或多次ab*c → ac, abc, abbc
^^^行首锚点^root 以root开头
$$$行尾锚点bash$ 以bash结尾
+\++1次或多次go+d → god, good
?\??0次或1次colou?r → color, colour
|||cat|dog
()\(\)()分组(ab)+ → ab, abab
{n,m}\{n,m\}{n,m}重复n到m次a{2,4} → aa, aaa, aaaa
[][][]字符类[abc] → a或b或c

9.2 常用字符类

[0-9]       数字            等价于 [[:digit:]]
[a-z]       小写字母        等价于 [[:lower:]]
[A-Z]       大写字母        等价于 [[:upper:]]
[a-zA-Z]    所有字母        等价于 [[:alpha:]]
[[:space:]] 空白字符(空格、Tab等)
[[:alnum:]] 字母和数字
[[:punct:]] 标点符号
[^abc]      排除a、b、c之外的字符(^在[]内表示取反)

9.3 锚点

^root         # 匹配以root开头的行
nologin$      # 匹配以nologin结尾的行
^$            # 匹配空行
^#            # 匹配注释行
^root.*bash$  # 以root开头且以bash结尾的行

互动提问.* 在正则中代表什么?

点击查看答案 `.` 匹配任意单个字符,`*` 表示0次或多次,所以 `.*` 匹配任意长度的任意字符串(包括空字符串)。它相当于正则里的"万能通配符"。

10 grep——文本搜索利器

生活类比

grep就像Ctrl+F的超级升级版,能在海量日志中瞬间定位你要的信息。

常用参数速查

参数作用参数作用
-i忽略大小写-n显示行号
-v反向匹配(排除)-c只统计匹配行数
-E使用扩展正则(ERE)-o只输出匹配部分
-r递归搜索目录-w全词匹配
-A n显示匹配行后n行-B n显示匹配行前n行
-C n显示匹配行前后n行-l只显示文件名

实战示例

# 1. 在/etc/passwd中查找使用bash的用户
grep "bash$" /etc/passwd
grep -c "bash$" /etc/passwd    # 统计有多少个

# 2. 查找日志中的错误(忽略大小写)
grep -i "error\|warning\|critical" /var/log/syslog

# 3. 排除注释行和空行,查看配置文件的有效内容
grep -v "^#\|^$" /etc/nginx/nginx.conf

# 4. 提取IP地址(使用ERE扩展正则 + -o只输出匹配部分)
grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' /var/log/nginx/access.log | sort -u

# 5. 查找哪个文件包含"listen"指令
grep -rn "listen" /etc/nginx/

# 6. 统计访问日志中各状态码出现次数
grep -Eo 'HTTP/[0-9.]+" [0-9]+' /var/log/nginx/access.log | \
    awk '{print $NF}' | sort | uniq -c | sort -rn

# 7. 查找占用内存最多的进程(前5)
ps aux | grep -v "grep" | sort -k4 -rn | head -5

11 sed——流编辑器(文本手术刀)

生活类比

sed就像一位外科医生:你告诉它"找到第5行,把'old'换成'new'",它就在文本流上精准"动刀"。

核心命令速查

命令作用示例
s替换s/old/new/g
d删除3d(删第3行)
a在行后追加/pattern/a\新文本
i在行前插入/pattern/i\新文本
p打印(配合-n)2,5p
c替换整行/pattern/c\新内容

11.1 替换(s命令)

# 基本替换(每行只替换第一个匹配)
echo "hello world hello" | sed 's/hello/hi/'
# 输出: hi world hello

# 全局替换(g标志)
echo "hello world hello" | sed 's/hello/hi/g'
# 输出: hi world hi

# 替换IP地址(修改配置文件)
sed -i 's/192.168.1.100/10.0.0.1/g' /etc/nginx/nginx.conf

# 使用不同的分隔符(处理路径时特别有用)
sed -i 's|/usr/local/nginx|/opt/nginx|g' nginx.conf

# 替换并备份原文件
sed -i.bak 's/old_port=80/new_port=8080/' config.txt

# 只替换第N个匹配
echo "aaa" | sed 's/a/A/2'    # 输出: aAa

11.2 删除(d命令)

# 删除空行
sed '/^$/d' file.txt

# 删除注释行(#开头)
sed '/^#/d' /etc/nginx/nginx.conf

# 删除第3行
sed '3d' file.txt

# 删除第2~5行
sed '2,5d' file.txt

# 删除最后一行
sed '$d' file.txt

# 删除匹配pattern的行
sed '/ERROR/d' app.log

11.3 插入与追加(i/a命令)

# 在"server {"行后追加内容
sed '/server {/a\    listen 8080;\n    server_name example.com;' nginx.conf

# 在包含"root"的行前插入注释
sed '/^root/i\# 这是管理员账户' /etc/passwd

# 在文件末尾追加一行
sed '$a\# End of file' config.txt

11.4 打印与地址范围

# 打印第2~5行(-n抑制默认输出)
sed -n '2,5p' /etc/passwd

# 打印包含root的行
sed -n '/root/p' /etc/passwd

# 打印从"BEGIN"到"END"之间的内容
sed -n '/BEGIN/,/END/p' data.txt

# 打印奇数行
sed -n '1~2p' file.txt

# 打印偶数行
sed -n '0~2p' file.txt

11.5 综合实战

#!/bin/bash
# 实战:分析并清洗Nginx日志

LOG="/var/log/nginx/access.log"

# 1. 提取所有404错误的URL
sed -n 's/.*"GET \(.*\) HTTP.*" 404 .*/\1/p' "$LOG" | sort -u

# 2. 批量修改配置文件中的端口
sed -i -e 's/listen 80;/listen 8080;/g' \
       -e 's/listen 443 ssl;/listen 8443 ssl;/g' \
       /etc/nginx/sites-available/*

# 3. 提取并格式化/etc/passwd信息
sed -n 's/^\([^:]*\):.*:\([^:]*\):[^:]*$/用户名=\1  HOME=\2/p' /etc/passwd

12 awk——文本分析利器(数据手术台)

生活类比

如果sed是剪刀,那awk就是一台带分析功能的流水线:它不仅能剪切,还能统计、计算、格式化——把每一行数据拆开来看,按需处理。

12.1 基本概念

awk的工作方式:逐行读取 → 拆分成字段 → 匹配模式 → 执行动作

默认分隔符:空格/Tab
$0  = 整行内容
$1  = 第一个字段
$2  = 第二个字段
$NF = 最后一个字段
NR  = 当前行号(Number of Record)
NF  = 当前行的字段数(Number of Fields)

12.2 基础用法

# 打印/etc/passwd的用户名和shell(用:分隔)
awk -F':' '{print $1, $7}' /etc/passwd

# 打印行号和内容
awk '{print NR": "$0}' file.txt

# 打印字段数大于6的行
awk -F':' 'NF>6{print $1}' /etc/passwd

# 只打印第2~5行
awk 'NR>=2 && NR<=5' /etc/passwd

# 打印最后一行
awk 'END{print NR}' file.txt

12.3 BEGIN与END块

# BEGIN:处理文件前执行(常用于打印表头)
# END:处理文件后执行(常用于打印汇总)

awk -F':' '
BEGIN {
    print "================================"
    print "    系统用户列表"
    print "================================"
    printf "%-15s %-8s %s\n", "用户名", "UID", "Shell"
    print "--------------------------------"
}
{
    if ($3 >= 1000 || $3 == 0) {
        printf "%-15s %-8s %s\n", $1, $3, $7
    }
}
END {
    print "================================"
    print "统计完成"
}' /etc/passwd

12.4 统计与计算示例

# 1. 统计文件行数、单词数、字符数(类似wc)
awk '{
    lines++
    words += NF
    chars += length($0) + 1   # +1是换行符
}
END {
    printf "行数: %d\n单词: %d\n字符: %d\n", lines, words, chars
}' file.txt

# 2. 统计Nginx访问日志中各HTTP状态码的次数
awk '{print $9}' /var/log/nginx/access.log | \
    awk '{count[$1]++} END{for(code in count) printf "%s: %d次\n", code, count[code]}'

# 3. 计算一列数字的总和、平均值、最大值、最小值
echo -e "85\n92\n78\n95\n88\n76\n90" | awk '
BEGIN { max=0; min=99999; sum=0 }
{
    sum += $1
    count++
    if ($1 > max) max = $1
    if ($1 < min) min = $1
}
END {
    printf "总分: %d\n", sum
    printf "平均: %.1f\n", sum/count
    printf "最高: %d\n", max
    printf "最低: %d\n", min
    printf "人数: %d\n", count
}'

# 4. 找出磁盘使用率最高的挂载点
df -h | awk 'NR>1{
    gsub(/%/,"",$5)
    if ($5+0 > max+0) { max=$5; mount=$6 }
}
END { print "最高使用率:", mount, max"%" }'

# 5. 统计每个IP的访问次数(Top 10)
awk '{print $1}' /var/log/nginx/access.log | \
    sort | uniq -c | sort -rn | head -10

12.5 awk内置变量速查

变量含义
$0当前整行
$1~$n第n个字段
NF当前行字段数
NR当前行号
FS输入分隔符(等价于 -F)
OFS输出分隔符
RS输入记录分隔符(默认换行)
ORS输出记录分隔符
FILENAME当前处理的文件名
length($0)当前行长度
# 用OFS控制输出格式
awk -F':' 'BEGIN{OFS=" | "}{print $1, $3, $7}' /etc/passwd

# 处理多个文件时区分来源
awk '{print FILENAME": "$0}' /etc/hosts /etc/resolv.conf

互动提问:sed和awk如何选择?

点击查看答案 简单替换/删除/插入用sed(更快更简洁);需要字段拆分、统计计算、复杂格式化输出用awk。两者也经常通过管道组合使用。

13 综合实战脚本

#!/bin/bash
# ========================================
# 服务器巡检脚本
# 功能:检查CPU、内存、磁盘、网络状态
# ========================================

# 颜色定义
RED='\033[31m'
GREEN='\033[32m'
YELLOW='\033[33m'
NC='\033[0m'

# 报告文件
REPORT="/tmp/server_report_$(date +%Y%m%d).txt"

# ---------- 函数定义 ----------

check_cpu() {
    echo "=== CPU使用率 ==="
    local idle
    idle=$(top -bn1 | grep "Cpu(s)" | awk '{print $8}' | tr -d '%')
    local usage
    usage=$(echo "100 - $idle" | bc 2>/dev/null || echo "N/A")
    echo "CPU使用率: ${usage}%"
}

check_memory() {
    echo "=== 内存使用情况 ==="
    free -m | awk 'NR==2{
        printf "总内存: %dMB\n已用: %dMB\n可用: %dMB\n使用率: %.1f%%\n",
        $2, $3, $4, $3/$2*100
    }'
}

check_disk() {
    echo "=== 磁盘使用情况 ==="
    df -h | awk 'NR>1{
        gsub(/%/,"",$5)
        flag = ($5+0 > 80) ? " [警告]" : " [正常]"
        printf "%-20s %5s  使用率: %s%%%s\n", $6, $2, $5, flag
    }'
}

check_process() {
    echo "=== Top5 CPU消耗进程 ==="
    ps aux --sort=-%cpu | head -6 | awk '
    NR==1{printf "%-8s %-6s %-6s %s\n", "USER", "%CPU", "%MEM", "COMMAND"}
    NR>1{printf "%-8s %-6s %-6s %s\n", $1, $3, $4, $11}
    '
}

# ---------- 主程序 ----------

{
    echo "============================================"
    echo "  服务器巡检报告"
    echo "  时间: $(date '+%Y-%m-%d %H:%M:%S')"
    echo "  主机: $(hostname)"
    echo "============================================"
    echo ""
    check_cpu
    echo ""
    check_memory
    echo ""
    check_disk
    echo ""
    check_process
    echo ""
    echo "============================================"
    echo "  报告生成完毕"
    echo "============================================"
} | tee "$REPORT"

echo ""
echo "报告已保存到: $REPORT"

运维常用命令精选

grep系列

grep -rn "error" /var/log/              # 递归搜索日志中的error
grep -v "^#" /etc/ssh/sshd_config       # 查看SSH有效配置
grep -c "404" access.log                # 统计404次数
grep -E "^(root|admin)" /etc/passwd     # ERE匹配root或admin开头

sed系列

sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config  # 关闭SELinux
sed -n '/server_name/,/}/p' nginx.conf     # 打印server_name到}之间的内容
sed '/^$/d;/^#/d' config.conf              # 删除空行和注释
sed -i '1i\#!/bin/bash' script.sh          # 在第一行插入shebang

awk系列

awk '{sum+=$1} END{print sum}' numbers.txt          # 求和
awk -F: '$3>=1000{print $1}' /etc/passwd           # 列出普通用户
awk '{print $1}' access.log | sort | uniq -c | sort -rn  # IP访问排行
df -h | awk '$5+0>80{print $6, $5}'               # 找出磁盘告警

管道组合拳

# 统计Nginx日志中访问量Top10的URL
awk '{print $7}' access.log | sort | uniq -c | sort -rn | head -10

# 找出TCP连接数最多的IP
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head

# 批量重命名.jpg为.png
for f in *.jpg; do mv "$f" "${f%.jpg}.png"; done

趣味命令专区

# 1. 用awk画一个爱心
awk 'BEGIN{
    for(y=15;y>=-15;y--){
        for(x=-30;x<=30;x++){
            a=x/10; b=y/7
            printf ((a^2+b^2-1)^3 - a^2*b^3 <= 0) ? "*" : " "
        }
        print ""
    }
}'

# 2. 终端里看矩阵雨(简化版)
cat /dev/urandom | tr -dc '01' | fold -w $(tput cols) | head -n 30

# 3. 用sed做"文本动画"
for i in {1..20}; do
    printf "%${i}s\n" ">>> Hello World <<<"
    sleep 0.1
done

# 4. 随机名言生成器
quotes=("纸上得来终觉浅,绝知此事要躬行" "学而不思则罔,思而不学则殆" "工欲善其事,必先利其器" "千里之行,始于足下")
echo "今日名言:${quotes[$((RANDOM % ${#quotes[@]}))]}"

# 5. 用awk生成随机密码
awk 'BEGIN{
    srand()
    chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%"
    len=length(chars)
    pwd=""
    for(i=1;i<=16;i++) pwd = pwd substr(chars, int(rand()*len)+1, 1)
    print "随机密码: " pwd
}'

# 6. 实时时钟(终端里看时间)
watch -n 1 'date "+%Y-%m-%d %H:%M:%S" | figlet 2>/dev/null || date "+%Y-%m-%d %H:%M:%S"'

小结:循环让重复工作自动化,正则让文本搜索精准化,sed让文本编辑批量化,awk让数据分析简单化。掌握这四个工具,Linux运维的80%文本处理任务都不在话下!