精读笔记 · 第29章 Shell 三剑客(grep / sed / awk)
大约 7 分钟
精读笔记 · 第29章 Shell 三剑客(grep / sed / awk)
精读整理自 RHCE9 随堂讲义(原文字版 38 页)|同名视频(第02套 Shell 编程) 关联知识文件:
分章笔记/01-Linux/02-07-Shell编程
一、正则表达式(RE)
- RE=字符模式,用于查找匹配指定字符;多数工具中写在两个正斜杠间,如
/l[oO]ve/;被 vim/sed/awk/grep 调用,mysql/oracle/php/python/Apache/Nginx 同样依赖正则。 - 元字符:表达不同于字面本身的含义。
- 校验数字输入脚本:
[[ $num =~ ^[0-9]+$ ]]配合 while 循环,非数字则报错重输,正确才退出。
基本正则元字符表
| 元字符 | 含义 | 示例 |
|---|---|---|
^ | 行首定位 | grep "^root"(比 grep root 少匹配行中间的 root) |
$ | 行尾定位 | love$ |
. | 任意单个字符 | a.c 可匹配 abc、adc |
* | 前导字符出现 0~n 次 | abc* 匹配 ab/abc/abcd… |
.* | 任意多个字符 | grep ".*" 1.txt |
[ ] | 范围内一个字符 | [lL]ove |
[ - ] | 连续范围 | [a-z0-9]([a-Z]=[a-zA-Z]) |
[^] | 匹配不在组内的字符(取反) | [^a-z]ove、[^0-9]ove |
\ | 转义元字符 | l\. 只匹配字面 l.ve |
\<、\> | 词首、词尾定位 | \<love 可命中行中的 love,^love 只命中行首 |
\(..\) | 分组标签,\1 引用 | :3,9 s/\(.*\)/#\1/ 给行加注释 |
x\{m\} | 字符 x 重复 m 次 | o\{3\} 只匹配 looove |
x\{m,\} | 重复 m 次以上 | o\{5,\} |
x\{m,n\} | 重复 m~n 次 | o\{5,10\}(egrep 中写作 o{5,10}) |
扩展正则元字符(egrep / grep -E / sed -r)
| 元字符 | 含义 | 示例 | | + | 前导字符出现 1~n 次 | lo+ve 匹配 love/loove,不含 lve | | ? | 前导字符出现 0~1 次 | lo?ve 匹配 love 与 lve | | \| | 或 | egrep "o\|v" | | () | 组字符(优先级高于 |) | egrep "love(able|rs)" 匹配 loveable/lovers | | \w\W | 字母数字、非字母数字 | l\w*ve≡l[a-zA-Z0-9]*ve | | \b | 词边界 | \bnorth\b≡\<north\> |
常用综合模式
/^love开头;/love$结尾;/l.ve;/lo*ve;/[Ll]ove;/love[^a-zA-Z0-9]后跟符号。/^$空行;/^[ \t]*$空白行;/^#、/^[ \t]*#定位注释行(含缩进注释)。/^[A-Z]..$大写开头 + 任意 2 字符结尾;vim 交换两词::1,$ s/\(square\) and \(fair\)/\2 and \1。
二、grep 家族
- grep 基本正则;egrep 扩展正则(\w/\W/\b);fgrep 固定字符串(不支持正则,
fgrep .按字面找点号)。 - 返回值:0=找到;1=未找到;2=出错(文件不存在等),脚本常用
echo $?判断。 - 参数速查: | 参数 | 作用 | 示例 | | --- | --- | --- | |
-q| 静默模式,只看 $? |grep -q "5\.." 1.txt| |-v| 反向取反 |grep -v "^#"| |-R| 递归查目录下所有文件 |grep -R xulei /home| |-o| 只输出命中的关键字 |grep -o "a" 1.txt| |-B2/-A2/-C2| 前 2 行 / 后 2 行 / 上下各 2 行 |grep -C2 root file| |-l| 只列文件名 |egrep -l 'root' /etc/passwd| |-n| 带行号输出 |egrep -n 'xulei' /etc/passwd| |-E/ egrep | 使用扩展正则 |egrep 'NW|EA' datafile|
三、sed 流编辑器
- 原理:Stream EDitor,非交互式;一次处理一行,当前行放入“模式空间”处理后送屏幕,默认不改原文件(重定向或
-i才写回)。 - 格式:
sed [options] 'command' file(s)或sed -f scriptfile file;扩展元字符(+ | ( ))需-r或转义。 - 返回值:只要无语法错误恒为 0。
- 定位:行号
3、范围3,$(3 到末尾)、$(最后一行)、正则/root/。
命令速查表
| 命令 | 作用 | 示例 |
|---|---|---|
d | 删除行 | 3d、/root/d、3,$d、2,$!d(! 反向保留 2 行起) |
s | 替换 | s/root/aofa/(首个)、s/root/aofa/g(本行全部) |
& | 引用整段命中 | s/[0-9][0-9]$/&.5/;vim :%s/.*/#&/g |
\(..\)+\1 | 分组引用 | s/(mail)/E\1/g;2,6s/(.*)/#\1/ |
r | 读入其他文件 | $r 1.txt(末尾)、/root/r 1.txt(root 行后) |
w | 另存为 | w 111.txt、1,5w 123.txt |
a | 行后追加 | 2a123;多行用 \ 续行 |
i | 行前插入 | 2iaaaaaaaa;多行同上 |
c | 整行替换 | 2caaaaaaaa;一行可换成多行 |
n | 读取下一行 | /root/{n;d} 删 root 的下一行;{n;n;d} 删下两行 |
{ } | 组合命令 | 3{h;d}(h 暂存空间);; 连接多条 |
-e | 多重编辑 | sed -r -e '1,3d' -e '4s/adm/admin/g' |
实战与变量
- 就地改配置(-i):删注释
sed -ri '/^#/d' vsftpd.conf;末尾追加sed -ri '$a\chroot_local_user=YES'($a=最后一行后追加);整行改写sed -ri '/^SELINUX=/cSELINUX=disabled' /etc/selinux/config;2~6 行加注释sed -r '2,6s/^/#/'。 - 引用外部变量必须用双引号:
sed -r "1a$var1" /etc/hosts(单引号不展开变量);$a(末行追加)与$var连用要转义:sed -r "\$a$var1"。
四、awk 文本处理语言
- 取名自作者 Alfred Aho、Peter Weinberger、Brian Kernighan 首字母;是编程语言:逐行扫描文件,匹配“模式”的行执行“动作”,未指定动作则打印整行。
- 原理:读一行→整行赋给
$0;再按分隔符(默认空格/Tab)切成字段$1、$2…(最多 100 个);处理完读下一行覆盖$0。 - 语法:
awk [options] 'commands' file(s);-F:自定义输入字段分隔符。 - 三段式:
BEGIN{}(读行前执行一次)+{}(每读一行执行一次)+END{}(全部行处理完后执行一次)。 - 内置变量: | 变量 | 含义 | 示例 | | --- | --- | --- | |
FS| 输入字段分隔符(默认空格) |-F:或BEGIN{FS=":"};-F'[ :\t]'多分隔符 | |OFS| 输出字段分隔符 |BEGIN{OFS="+++"}后 print 逗号变 +++ | |RS/ORS| 输入/输出记录(行)分隔符,默认换行 |BEGIN{RS=" "}记录按空格切分 | |NF| 当前行字段总数 |$NF=最后一个字段 | |NR| 多文件累计行号 |{print NR,$0}| |FNR| 各文件独立行号 | 多文件时从 1 重新计数 | - 格式化输出:
print "文字" $n(文字加引号)、\n换行、\t制表;print $1,$3的逗号按 OFS 拼接。 - 模式与动作:
- 字符串/正则比较:
awk '/^root/'、$0 ~ /^root/、$0 !~ /^root/、-F: '$1 ~ /^root/'。 - 关系运算符
< <= == != >= >:数值$3==0、$3<10;字符串$7=="/bin/bash"、$1=="alice"。 - 算术
+ - * / % ^:awk -F: '$3*10>500'。 - 逻辑
&& || !:$1~/root/ && $3<=15、!($1~/root/ || $3<=15)。 - 范围模式
/开始/,/结束/:awk -F: '/adm/,/lpd/' /etc/passwd(注意存在重复匹配行现象)。
- 字符串/正则比较:
- 变量与流程控制:
- 内置变量传入:
awk -v user=root -F: '$1==user'。 - shell 变量拼进双引号:
awk '$1 ~ "'"$heihei"'"' passwd。 - if 计数:
awk -F: '{if($3==0){count++}else{i++}} END{print "管理员数:"count;print "系统用户数:"i}' /etc/passwd。 - 三类用户判断:
if($3==0) 管理员 else if($3>999) 普通用户 else 系统用户,可输出姓名或统计数量。 - 字段可改写:
awk -F: '$3==0{$3="chaoji";print $0}';三元$7>4 ? "high " : "low ";复合赋值$8+=12、$7%=3。
- 内置变量传入:
五、命令速查表(实战一句话)
| 场景 | 写法 |
|---|---|
| 查关键字/带行号 | grep -n "root" /etc/passwd |
| 全词/取反/递归/静默 | egrep '\<root\>' f、grep -v、grep -R、grep -q |
| 只输出命中内容 | grep -o "a" f |
| 删注释/删行 | sed -ri '/^#/d' f、sed '3,$d' f |
| 替换全部并写回 | sed -ri 's/old/new/g' f |
| 行末追加配置 | sed -ri '$a\chroot_local_user=YES' f |
| 提取字段 | awk -F: '{print $1,$3}' /etc/passwd |
| 条件统计 | awk -F: '{if($3==0)c++}END{print c}' /etc/passwd |
| 范围输出 | awk '/start/,/end/' file |
六、易错点
- grep 返回值 0=找到、1=无匹配、2=出错,脚本判断别写反。
*作用于“前导字符”,abc*匹配 ab/abc…;“任意多个字符”要写.*。[^]在方括号内是取反;^在括号外才是行首。- sed 默认不改文件,持久化必须
-i或重定向;扩展元字符须-r。 s/x/y/g的 g 只管“本行全部”,不带 g 只替换每行第一个。&是整段命中、\1是分组命中;路径含/时把分隔符换成#。- sed 用外部变量要双引号;
$a(末行追加)防展开写成\$a。 - awk 中 FS/OFS 管字段、RS/ORS 管记录、NF 字段数、NR 累计号、FNR 独立号,别混淆。
- awk 的
$1/$NF是字段;经-v user=root传入的变量名不带$。 - awk 模式里
==比较数字或字符串都要加双引号(字符串),数值直接写。
七、实操清单
正则校验数字输入脚本 → grep 基本/扩展元字符各 2~3 例 → egrep 对 datafile 练综合模式 10+ 条 → 返回值 echo $? 实验 → fgrep 与 grep 对比 → sed 的 d/s/a/i/c/r/w/n/!/{}/-e 各一例 → -ri 实战(vsftpd/selinux/加注释)→ sed 外部变量(单双引号、$a 转义)→ awk 三段式 BEGIN/{}/END → FS/OFS/RS/NR/FNR/NF 输出对比 → 正则/关系/算术/逻辑/范围模式练习 → if 统计三类用户数量与姓名。
八、本章自测
^、[^]、$、\<各自含义?abc*与ab.*有何区别?- grep / egrep / fgrep 区别?返回值 0/1/2 分别代表什么?
- sed 的行号与正则定位写法?哪些命令用于增删改查?如何真正写入文件?
&与\1的作用?sed 分隔符如何更换?- awk 的 BEGIN/{}/END 三段分别在何时执行?
- FS/OFS/RS/NF/NR/FNR 分别代表什么?举例说明。
- 用 awk 统计 /etc/passwd 中管理员、系统用户、普通用户数量。
- 在 sed 与 awk 中如何引用 shell 变量?各有什么坑?
