正则表达式及文本处理三剑客【1.6】

正则表达式及文本处理三剑客【1.6】

7.3 正则表达式

REGEXP: Regular Expressions,由一类特殊字符及文本字符所编写的模式;其中有些字符(元字符)
不表示字符字面意义,而表示控制或通配的功能,类似于增强版的通配符功能;但与通配符不同,通配
符功能是用来处理文件名,而正则表达式是处理文本内容中字符;

  • 正则表达式被很多程序和开发语言所广泛支持:vim, less,grep,sed,awk, nginx,mysql 等;
  • 正则表达式分两类:
    • 基本正则表达式:BRE Basic Regular Expressions;
    • 扩展正则表达式:ERE Extended Regular Expressions;
  • 正则表达式引擎:
    • 采用不同算法,检查处理正则表达式的软件模块,如:PCRE(Perl Compatible Regular Expressions)
  • 正则表达式的元字符分类:
    • 字符匹配
    • 匹配次数
    • 位置锚定
    • 分组
  • 帮助:man 7 regex

7.3.1 基本正则表达式元字符

7.3.1.1 字符匹配
常用选项含义
.匹配任意单个字符(除了\n),可以是一个汉字或其它国家的文字
[]匹配指定范围内的任意单个字符,示例:[wang] [0-9] [a-z] [a-zA-Z]
[^]匹配指定范围外的任意单个字符,示例:[^wang]
[:alnum:]字母和数字
[:alpha:]代表任何英文大小写字符,亦即 A-Z, a-z
[:lower:]小写字母,示例:[[:lower:]],相当于[a-z]
[:upper:]大写字母
[:blank:]空白字符(空格和制表符)
[:space:]包括空格、制表符(水平和垂直)、换行符、回车符等各种类型的空白,比[:blank:]包含的范围广
[:cntrl:]不可打印的控制字符(退格、删除、警铃…)
[:digit:]十进制数字
[:xdigit:]十六进制数字
[:graph:]可打印的非空白字符
[:print:]可打印字符
[:punct:]标点符号
---------------------------------------
\s匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [\f\r\t\v],Unicode正则表达式会匹配全角空格符
\S匹配任何非空白字符。等价于 [^\f\r\t\v]
\w匹配一个字母,数字,下划线,汉字,其它国家文字的字符,等价于[_[:alnum:]字]
\W匹配一个非字母,数字,下划线,汉字,其它国家文字的字符,等价于[^_[:alnum:]字]
  • 范例:匹配任意单个字符
[root@ubuntu2204 ~]# grep "r.t" /etc/passwd

[root@ubuntu2204 ~]# grep "r..t" /etc/passwd
root:x:0:0:root:/root:/bin/bash

[root@rocky8 ~]# grep "r.t" /etc/passwd
operator:x:11:0:operator:/root:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin

[root@ubuntu2204 ~]# echo a你好b | grep a..b
a你好b

[root@ubuntu2204 ~]# echo aaa你好b | grep a..b
aaa你好b

[root@ubuntu2204 ~]# echo a你-b | grep a..b
a你-b
  • 范例:范围匹配
[root@centos8 ~]#ls /etc/ | grep 'rc[.0-6]'
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d
rc.d
rc.local

[root@centos8 ~]#ls /etc/ | grep 'rc[.0-6].'
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d
rc.d
rc.local

[root@centos8 ~]#ls /etc/ | grep 'rc[.0-6]\.'
rc0.d
rc1.d
rc2.d
rc3.d
rc4.d
rc5.d
rc6.d

[root@ubuntu2204 ~]# echo ma | grep '[lnm]'
ma

[root@ubuntu2204 ~]# echo ma | grep '[^lnm]'
ma

[root@ubuntu2204 ~]# echo "ab1c2d3e4444fABCg$%^" | grep "[0-Z]"
ab1c2d3e4444fABCg$%^

[root@ubuntu2204 ~]# echo "ab1c2d3e4444fABCg$%^" | grep "[0-Z]"
ab1c2d3e4444fABCg$%^

[root@ubuntu2204 ~]# echo "ab1c2d3e4444fABCg$%^" | grep "[0-Z]"
ab1c2d3e4444fABCg$%^

[root@ubuntu2204 ~]# echo "ab1c2d3e4444fABCg$%^" | grep "[0-9]"
ab1c2d3e4444fABCg$%^

[root@ubuntu2204 ~]# echo "ab1c2d3e4444fABCg$%^" | grep "[a-z]"
ab1c2d3e4444fABCg$%^

[root@ubuntu2204 ~]# echo "ab1c2d3e4444fABCg$%^" | grep "[A-Z]"
ab1c2d3e4444fABCg$%^

[root@ubuntu2204 ~]# echo "ab1c2d3e4444fABCg$%^" | grep "[^0-Z]"
ab1c2d3e4444fABCg$%^

[root@ubuntu2204 ~]# echo "ab1c2d3e4444fABCg$%^" | grep "[[:punct:]]"
ab1c2d3e4444fABCg$%^

#匹配空格
[root@ubuntu2204 ~]# echo "1 2 3" | grep "[[:blank:]]"
1 2 3

#匹配非空格
[root@ubuntu2204 ~]# echo "1 2 3" | grep "[^[:blank:]]"
1 2 3
7.3.1.2 匹配次数
  • 用在要指定次数的字符后面,用于指定前面的字符要出现的次数
常用选项含义
*匹配前面的字符任意次,包括0次,贪婪模式:尽可能长的匹配
.*任意长度的任意字符
\?匹配其前面的字符出现0次或1次,即:可有可无
\+匹配其前面的字符出现最少1次,即:肯定有且 >=1 次
\{n\}匹配前面的字符n次
\{m,n\}匹配前面的字符至少m次,至多n次
\{,n\}匹配前面的字符至多n次,<=n
\{n,\}匹配前面的字符至少n次
x\?
# 相当于
x\{,1\}

x\+
# 相当于
x\{1,\}
  • 范例:
[root@ubuntu2204 ~]# echo "rt" | grep "ro*t"
rt

[root@ubuntu2204 ~]# echo "rot" | grep "ro*t"
rot

[root@ubuntu2204 ~]# echo "root" | grep "ro*t"
root

[root@ubuntu2204 ~]# echo "roooooooot" | grep "ro*t"
roooooooot
  • 范例:任意长度任意字符
[root@ubuntu2204 ~]# grep "r.*t" /etc/passwd
  • 范例:0次或1次
[root@ubuntu2204 ~]# echo /etc/ |grep "/etc/\?"
/etc/

[root@ubuntu2204 ~]# echo /etc |grep "/etc/\?"
/etc

[root@Rockey9-1 ~]# echo "abcd" | grep "abcd\?"
abcd
[root@Rockey9-1 ~]# echo "abc" | grep "abcd\?"
abc
[root@Rockey9-1 ~]# echo "abcef" | grep "abcd\?"
abcef
[root@Rockey9-1 ~]# echo "abcddd" | grep "abcd\?"
abcddd

[root@Rockey9-1 ~]# echo "abd" | grep "abcd\?"
[root@Rockey9-1 ~]# echo "acd" | grep "abcd\?"
  • 范例:1次或多次
[root@ubuntu2204 ~]# echo google | grep "go\+gle"
google

[root@ubuntu2204 ~]# echo gogle | grep "go\+gle"
gogle

[root@ubuntu2204 ~]# echo ggle | grep "go\+gle"

[root@ubuntu2204 ~]# echo gooogle | grep "go\+gle"
gooogle
  • 范例:自定义次数
#2[root@ubuntu2204 ~]# echo google | grep "go\{2\}gle"
google

#2次到5[root@ubuntu2204 ~]# echo google | grep "go\{2,5\}gle"
google

#2次到多次
[root@ubuntu2204 ~]# echo google | grep "go\{2,\}gle"
google

#0次到2[root@ubuntu2204 ~]# echo google | grep "go\{,2\}gle"
google
  • 范例: 匹配正负数
[root@ubuntu2204 ~]# echo -1 -2 123 -123 234 |grep '-\?[0-9]\+'
grep: invalid option -- '\'
Usage: grep [OPTION]... PATTERNS [FILE]...
Try 'grep --help' for more information.

[root@ubuntu2204 ~]# echo -1 -2 123 -123 234 |grep '\-\?[0-9]\+'
-1 -2 123 -123 234

[root@ubuntu2204 ~]# echo -1 -2 123 -123 234 |grep -E '-?[0-9]+'
grep: invalid option -- '?'
Usage: grep [OPTION]... PATTERNS [FILE]...
Try 'grep --help' for more information.

[root@ubuntu2204 ~]# echo -1 -2 123 -123 234 |grep -E '\-?[0-9]+'
-1 -2 123 -123 234

[root@ubuntu2204 ~]# echo -1 -2 123 -123 234 |grep -E -- '-?[0-9]+'
-1 -2 123 -123 234

[root@ubuntu2204 ~]# echo -1 -2 123 -123 234 |grep -E '(-)?[0-9]+'
-1 -2 123 -123 234

[root@Rockey9-1 ~]# echo -1 -2 123 -123 234 |grep '\-\{1\}[0-9]\+'
-1 -2 123 -123 234
[root@Rockey9-1 ~]# echo -1 -2 123 -123 234 |grep '[\-][0-9]\+'
-1 -2 123 -123 234

在这里插入图片描述

  • 范例: 取IP地址
[root@ubuntu2204 ~]# ifconfig ens33
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
		inet 10.0.0.206 netmask 255.255.255.0 broadcast 10.0.0.255
		inet6 fe80::20c:29ff:fe11:98d9 prefixlen 64 scopeid 0x20<link>
		ether 00:0c:29:11:98:d9 txqueuelen 1000 (Ethernet)
		RX packets 13925 bytes 1496342 (1.4 MB)
		RX errors 0 dropped 0 overruns 0 frame 0
		TX packets 13185 bytes 3218569 (3.2 MB)
		TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

[root@ubuntu2204 ~]# ifconfig ens33|grep netmask |grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}'|head -n1
10.0.0.206

[root@ubuntu2204 ~]# ifconfig ens33|grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}'|head -n1
10.0.0.206

在这里插入图片描述

7.3.1.3 位置锚定
  • 位置锚定可以用于定位出现的位置
^ 				#行首锚定, 用于模式的最左侧
$ 				#行尾锚定,用于模式的最右侧
^PATTERN$ 		#用于模式匹配整行
^$ 				#空行
^[[:space:]]*$ 	#空白行
\< 或 \b 		#词首锚定,用于单词模式的左侧
\> 或 \b 		#词尾锚定,用于单词模式的右侧
\<PATTERN\> 	#匹配整个单词

#注意: 单词是由字母,数字,下划线组成
  • 范例:
#以 # 开头
[root@ubuntu2204 ~]# grep "^#" /etc/fstab

#以 # 结尾
[root@Rockey9-1 ~]# grep "#$" /etc/fstab

#以 bash 结尾
[root@ubuntu2204 ~]# grep "bash$" /etc/passwd

#所有空行
[root@ubuntu2204 ~]# grep "^$" /etc/profile

#所有非空行
[root@ubuntu2204 ~]# grep -v "^$" /etc/profile

#所有非注释行
[root@ubuntu2204 ~]# grep "^[^#]" /etc/profile

#排除所有空行和注释行
[root@ubuntu2204 ~]# grep -v "^$" /etc/profile | grep -v "^#"

#同上
[root@ubuntu2204 ~]# grep -v "^$" /etc/profile | grep -v "^#"

#同上
[root@ubuntu2204 ~]# grep -v -e "^$" /etc/profile -v -e "^#"

#同上
[root@ubuntu2204 ~]# grep -v '^$\|^#' /etc/profile
[root@Rockey9-1 ~]# grep -v "^#\|^$" /etc/fstab



#匹配单词
[root@ubuntu2204 ~]# echo mage | grep "\<mage\>"

[root@ubuntu2204 ~]# echo mage- | grep "\<mage\>"

[root@ubuntu2204 ~]# echo magee | grep "\<mage\>"
[root@Rockey9-1 ~]# vim abc
[root@Rockey9-1 ~]# cat -A abc
111 $
1234$
$
$
 $
      $
aaa$
^I$
 ^I$
bb$
endl$
[root@Rockey9-1 ~]# grep "^$" abc


[root@Rockey9-1 ~]# grep "^$" abc | cat -A
$
$
[root@Rockey9-1 ~]# grep "^ $" abc | cat -A
 $
[root@Rockey9-1 ~]# grep "^ \+$" abc | cat -A
 $
      $
  • 范例:
[root@centos8 ~]#grep '^[^#]' /etc/fstab
UUID=acf9bd1f-caae-4e28-87be-e53afec61347 / xfs defaults 0 0
UUID=1770b87e-db5a-445e-bff1-1653ac64b3d6 /boot ext4 defaults 1 2
UUID=ffffd919-d674-44d9-a4e7-402874f0a1f0 /data xfs defaults 0 0
UUID=409e36d2-ac5e-423f-ad78-9b12db4576bd swap swap defaults 0 0

[root@ubuntu2204 ~]# grep '^[^#]' /etc/fstab
/dev/disk/by-id/dm-uuid-LVM-
2DfhG2JAeOckxicJorCIykZWj5F57OAw1wUxIh2DKVkhusX3fhgEVlJJIPV3tnGn / ext4 defaults 0 1
/dev/disk/by-uuid/195992f6-95be-4629-b331-8fb09cf99819 /boot ext4 defaults 0 1
/swap.img none swap sw 0 0
7.3.1.4 分组其它
7.3.1.4.1 分组
  • 分组:() 将多个字符捆绑在一起,当作一个整体处理,如:(root)+
    -后向引用:分组括号中的模式匹配到的内容会被正则表达式引擎记录于内部的变量中,这些变量的命名
  • 方式为: \1, \2, \3, …
  • \1 表示从左侧起第一个左括号以及与之匹配右括号之间的模式所匹配到的字符
  • 注意: \0 表示正则表达式匹配的所有字符
  • 示例:
#ab,然后c出现3[root@ubuntu2204 ~]# echo abccc | grep "abc\{3\}"
abccc

#abc作为一个整体出现3[root@ubuntu2204 ~]# echo abcabcabc | grep "\(abc\)\{3\}"
abcabcabc

#后向引用
[root@ubuntu2204 ~]# echo abcabcabc | grep "\(abc\)\1"
abcabcabc

[root@ubuntu2204 ~]# echo abcabcabc | grep "\(abc\)\1\1"
abcabcabc

#\1表示引用第一个分组的内容,即 abc
[root@ubuntu2204 ~]# echo "abcdefabc" | grep "\(abc\)def\1"
abcdefabc

#\1表示引用第一个分组的内容,即abc,\{3\}表示引用内容出现3[root@ubuntu2204 ~]# echo abc-def-abcabcabc | grep "^\(abc\)-\(def\)-\1\{3\}"
abc-def-abcabcabc
[root@ubuntu2204 ~]# echo abc-def-abcabcabc-def-abc-defdef | grep "^\(abc\)-\(def\)-\1\{3\}-\2-\1-\2\{2\}"
abc-def-abcabcabc-def-abc-defdef

#取IP
[root@ubuntu2204 ~]# ifconfig ens33 | grep -o "\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}" | head -n 1
10.0.0.158
  • 注意: 后向引用引用前面的分组括号中的模式所匹配字符,而非模式本身
7.3.1.4.2 或者
  • 或者:|
  • 示例:
a\|b 		#a或b
C\|cat 		#C或cat
\(C\|c\)at 	#Cat或cat
#a或b a\|b
[root@ubuntu2204 ~]# echo "a" | grep "a\|b"
a
[root@ubuntu2204 ~]# echo "b" | grep "a\|b"
b
[root@ubuntu2204 ~]# echo "ab" | grep "a\|b"
ab
[root@ubuntu2204 ~]# echo "abc" | grep "a\|b"
abc
[root@ubuntu2204 ~]# echo "cab" | grep "a\|b"
cab
[root@ubuntu2204 ~]# echo "acb" | grep "a\|b"
acb


#12a 或 b 12a\|b
[root@ubuntu2204 ~]# echo "12a" | grep "12a\|b"
12a
[root@ubuntu2204 ~]# echo "b" | grep "12a\|b"
b
[root@ubuntu2204 ~]# echo "12ab" | grep "12a\|b"
12ab
[root@ubuntu2204 ~]# echo "12ba" | grep "12a\|b"
12ba


#12a 或 12b 12a\|12b
[root@ubuntu2204 ~]# echo "12a" | grep "12a\|12b"
12a
[root@ubuntu2204 ~]# echo "12b" | grep "12a\|12b"
12b
[root@ubuntu2204 ~]# echo "12ab" | grep "12a\|12b"
12ab
[root@ubuntu2204 ~]# echo "12ba" | grep "12a\|12b"
12ba


#12a 或 12b 12\(a\|b\)
[root@ubuntu2204 ~]# echo "12a" | grep "12\(a\|b\)"
12a
[root@ubuntu2204 ~]# echo "12b" | grep "12\(a\|b\)"
12b
[root@ubuntu2204 ~]# echo "12ab" | grep "12\(a\|b\)"
12ab
[root@ubuntu2204 ~]# echo "12ba" | grep "12\(a\|b\)"
12ba


#12a 或 12b 12[ab]
[root@ubuntu2204 ~]# echo "12a" | grep "12[ab]"
12a
[root@ubuntu2204 ~]# echo "12b" | grep "12[ab]"
12b
[root@ubuntu2204 ~]# echo "12ab" | grep "12[ab]"
12ab
[root@ubuntu2204 ~]# echo "12ba" | grep "12[ab]"
12ba
  • 范例:排除空行和#开头的行
[root@ubuntu2204 ~]# grep -v '^#' /etc/apache2/apache2.conf |grep -v ^$
[root@ubuntu2204 ~]# grep -v '^#\|^$' /etc/apache2/apache2.conf
[root@ubuntu2204 ~]# grep -v '^\(#\|$\)' /etc/apache2/apache2.conf
[root@ubuntu2204 ~]# grep "^[^#]" /etc/apache2/apache2.conf

7.3.2 扩展正则表达式元字符

7.3.2.1 字符匹配
常用选项含义
.任意单个字符
[wang]指定范围的字符
[^wang]不在指定范围的字符
[:alnum:]字母和数字
[:alpha:]代表任何英文大小写字符,亦即 A-Z, a-z
[:lower:]小写字母,示例:[[:lower:]],相当于[a-z]
[:upper:]大写字母
[:blank:]空白字符(空格和制表符)
[:space:]水平和垂直的空白字符(比[:blank:]包含的范围广)
[:cntrl:]不可打印的控制字符(退格、删除、警铃…)
[:digit:]十进制数字
[:xdigit:]十六进制数字
[:graph:]可打印的非空白字符
[:print:]可打印字符
[:punct:]标点符号
7.3.2.2 匹配次数
常用选项含义
*匹配前面字符任意次
?0或1次
+1次或多次
{n}匹配n次
{m,n}至少m,至多n次
7.3.2.3 位置锚定
常用选项含义
^行首
$行尾
\<, \b词首
\>, \b词尾
7.3.2.4 分组其它
() 分组 	#后向引用:\1, \2, ... 注意: \0 表示正则表达式匹配的所有字符
| 			#或者
a|b 		#a或b
C|cat 		#C或cat
(C|c)at 	#Cat或cat
  • 范例:
[root@ubuntu2204 ~]# ifconfig | grep -Ewo "(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])"|head -n1
10.0.0.8

7.4 文本处理三剑客

  • grep 命令主要对文本的(正则表达式)行基于模式进行过滤
  • sed:stream editor,文本编辑工具
  • awk:Linux上的实现gawk,文本报告生成器

7.4.1 文本处理三剑客之 grep

  • grep: Global search REgular expression and Print out the line
  • 作用:文本搜索工具,根据用户指定的 “模式” 对目标文本逐行进行匹配检查;打印匹配到的行
  • 模式:由正则表达式字符及文本字符所编写的过滤条件
  • 帮助:https://man7.org/linux/man-pages/man1/grep.1.html
  • 格式:
grep [OPTIONS...] PATTERN [FILE...]
常用选项含义
-E|–extended-regexp使用ERE,相当于egrep
-F|–fixed-strings不支持正则表达式,相当于fgrep
-G|–basic-regexp将样式视为普通的表示法来使用
-P|–perl-regexp支持Perl格式的正则表达式
-e|–regexp=PATTERN实现多个选项间的逻辑or关系,如:grep –e ‘cat ’ -e ‘dog’ file
-f|–file=FILE从文件中读取匹配规则,每行一条
-i|–ignore-case忽略字符大小写
-w|–word-regexp匹配整个单词
-x|–line-regexp整行匹配
-s|–no-messages不显示错误信息
-v|–invert-match显示没有被匹配上的行,即取反
-B|–before-context=N显示匹配到的字符串所在的行及其前N行
-A|–after-context=N显示匹配到的字符串所在的行及其后n行
-C|–context=N显示匹配到的字符串所在的行及其前后各N行
-N同 --context=N
–color=auto对匹配到的内容高亮显示[always
-m|–max-count=N只匹配N行,是行,不是次数,一行可能匹配两个,但是,这里是行
-b|–byte-offset显示匹配行第一个字符的编号
-n|–line-number显示匹配的行号
-H|–with-filename显示匹配行所在的文件名
-h|–no-filename不显示匹配行所在的文件名
-o|–only-matching仅显示匹配到的字符串
-q|–quiet|–silent静默模式,不输出任何信息,结果要从变量$?拿
–binary-files=TYPE指定二进制文件类型 [binary|text|without-match]
-a|–text同 --binary-files=text
-I同 --binary-files=without-match
-d|–directories=ACTION怎样查找目录 [read|recurse|skip]
-D|–devices=ACTION怎样查找设备文件 [read|skip]
-r|–recursive递归目录,但不处理软链接
-R|–dereference-recursive递归目录,但处理软链接
-L|–files-without-match显示没有匹配上的文件名,只显示文件名
-l|–files-with-matches显示匹配上的文件名,只显示文件名
-c|–count统计匹配的行数,是行数,一行可以匹配一次到多次
  • 范例:标准输入
[root@ubuntu2204 ~]# grep hello
123hello456
123hello456

[root@ubuntu2204 ~]# grep hello -
123hello456
123hello456
  • 范例:处理文件
[root@ubuntu2204 ~]# grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
  • 范例:管道
[root@ubuntu2204 ~]# cat /etc/passwd | grep root
root:x:0:0:root:/root:/bin/bash
  • 范例:标准输入重定向
[root@ubuntu2204 ~]# grep root < /etc/passwd
root:x:0:0:root:/root:/bin/bash
  • 范例:取前三行
[root@ubuntu2204 ~]# grep -m 3 bin /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
  • 范例:取反,取不匹配的行
[root@ubuntu2204 ~]# grep -v nologin /etc/passwd
root:x:0:0:root:/root:/bin/bash
sync:x:4:65534:sync:/bin:/bin/sync
pollinate:x:105:1::/var/cache/pollinate:/bin/false


#不看注释行
[root@ubuntu2204 ~]# grep -v "#" /etc/fstab
  • 范例:不区分大小写
[root@ubuntu2204 ~]# grep -i ROOt /etc/passwd
root:x:0:0:root:/root:/bin/bash
  • 范例:显示行号
[root@ubuntu2204 ~]# grep -n bash /etc/passwd
1:root:x:0:0:root:/root:/bin/bash
32:mage:x:1000:1000:mage:/home/mage:/bin/bash
34:jose:x:1001:1001::/home/jose:/bin/bash
35:tom:x:1002:1002::/home/tom:/bin/bash
36:jerry:x:1003:1003::/home/jerry:/bin/bash
  • 范例:显示匹配的行数
[root@ubuntu2204 ~]# grep -c bash /etc/passwd
5
  • 范例:仅显示匹配到的内容
[root@ubuntu2204 ~]# grep -o root /etc/passwd
root
root
root
  • 范例:静默模式
[root@ubuntu2204 ~]# grep -q root /etc/passwd
[root@ubuntu2204 ~]# echo $?
0

[root@ubuntu2204 ~]# grep -q roooot /etc/passwd
[root@ubuntu2204 ~]# echo $?
1
  • 范例:显示匹配到的行及后两行
[root@rocky86 ~]# grep -A 2 root /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
--
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

[root@rocky86 ~]# grep -A 1 root /etc/passwd
  • 范例:显示匹配到的行及前两行
[root@rocky86 ~]# grep -B 2 root /etc/passwd
root:x:0:0:root:/root:/bin/bash
--
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
  • 范例:显示匹配到的行及前后各两行
[root@rocky86 ~]# grep -C 2 root /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
--
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
  • 范例:显示匹配root的行或匹配 bash 的行
[root@ubuntu2204 ~]# grep -e root -e bash /etc/passwd
root:x:0:0:root:/root:/bin/bash
mage:x:1000:1000:mage:/home/mage:/bin/bash
jose:x:1001:1001::/home/jose:/bin/bash
tom:x:1002:1002::/home/tom:/bin/bash
jerry:x:1003:1003::/home/jerry:/bin/bash
  • 范例:显示匹配root的行且匹配 bash 的行
[root@ubuntu2204 ~]# grep root /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
  • 范例:从文件读取匹配规则
[root@ubuntu2204 ~]# cat test.txt
root
bash
[root@ubuntu2204 ~]# grep -f test.txt /etc/passwd
root:x:0:0:root:/root:/bin/bash
mage:x:1000:1000:mage:/home/mage:/bin/bash
jose:x:1001:1001::/home/jose:/bin/bash
tom:x:1002:1002::/home/tom:/bin/bash
jerry:x:1003:1003::/home/jerry:/bin/bash
  • 范例:递归匹配
#不处理链接
[root@ubuntu2204 ~]# grep -r root /etc/*

#处理链接
[root@ubuntu2204 ~]# grep -R root /etc/*
  • 范例:只显示有匹配到的文件的文件名,不显示具体内容
[root@ubuntu2204 ~]# grep root -l /etc/passwd /etc/sudoers /etc/my.cnf
/etc/issue
/etc/passwd
/etc/sudoers
grep: /etc/my.cnf: No such file or directory
  • 范例:显示内容来自于哪个文件
[root@ubuntu2204 ~]# grep -H root /etc/passwd /etc/sudoers /etc/my.cnf
/etc/issue
/etc/passwd:root:x:0:0:root:/root:/bin/bash
/etc/sudoers:# This file MUST be edited with the 'visudo' command as root.
/etc/sudoers:# This preserves proxy settings from user environments of root
/etc/sudoers:# While you shouldn't normally run git as root, you need to with
etckeeper
/etc/sudoers:# Per-user preferences; root won't have sensible values for them.
/etc/sudoers:root ALL=(ALL:ALL) ALL
/etc/sudoers:# Members of the admin group may gain root privileges
grep: /etc/my.cnf: No such file or directory
  • 范例:命令行展开
[root@ubuntu2204 ~]# grep `whoami` /etc/passwd
root:x:0:0:root:/root:/bin/bash

[root@ubuntu2204 ~]# grep $(whoami) /etc/passwd
root:x:0:0:root:/root:/bin/bash

[root@ubuntu2204 ~]# echo Linux123 | grep $(uname)
Linux123
  • 范例:变量展开
[root@ubuntu2204 ~]# grep "$USER" /etc/passwd
root:x:0:0:root:/root:/bin/bash
  • 范例:取CPU核数
[root@ubuntu2204 ~]# grep -c processor /proc/cpuinfo
2
[root@ubuntu test]# cat /proc/cpuinfo | grep "processor"
processor	: 0
processor	: 1
[root@ubuntu test]# cat /proc/cpuinfo | grep -c "processor"
2
[root@ubuntu test]# cat /proc/cpuinfo | grep "processor" | wc -l
2
  • 范例: 分区利用率最大的值
[root@ubuntu2204 ~]# df | grep '^/dev/sd' |tr -s ' ' %|cut -d% -f5|sort -n|tail -1
14
[root@ubuntu2204 ~]# df |grep '^/dev/sd' |grep -oE '\<[0-9]{,3}%'|tr -d '%'|sort -nr|head -n1
14

[root@ubuntu2204 ~]# df |grep '^/dev/sd' |grep -oE '\<[0-9]{,3}%'|grep -Eo '[0-9]+' |sort -nr|head -n1
14
  • 范例: 哪个IP和当前主机连接数最多的前三位
root@jose-404:~# ss -nt | grep "^ESTAB" |tr -s ' ' : |cut -d: -f6|sort |uniq -c|sort -nr|head -n3
	2 106.39.148.233
	1 100.100.30.26
  • 范例: 连接状态的统计
root@jose-404:~# ss -nta | grep -v '^State' |cut -d" " -f1|sort |uniq -c
	3 ESTAB
	5 LISTEN
	4 TIME-WAIT
root@jose-404:~# ss -nta | tail -n +2 |cut -d" " -f1|sort |uniq -c
	3 ESTAB
	5 LISTEN
	1 TIME-WAIT
[root@ubuntu2204 ~]# grep -v "^#" /etc/profile | grep -v '^$'
[root@ubuntu2204 ~]# grep -v "^#\|^$" /etc/profile
[root@ubuntu2204 ~]# grep -v "^\(#\|$\)" /etc/profile
[root@ubuntu2204 ~]# grep -Ev "^(#|$)" /etc/profile
[root@ubuntu2204 ~]# egrep -v "^(#|$)" /etc/profile
[root@centos8 ~]#grep -o 'r..t' /etc/passwd
root
root
root
root
r/ft
rypt
[root@ubuntu2204 ~]# ifconfig | grep -E '[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}'
	inet 10.0.0.206 netmask 255.255.255.0 broadcast 10.0.0.255
	RX packets 16993 bytes 3820791 (3.8 MB)
	TX packets 15194 bytes 3563956 (3.5 MB)
	inet 127.0.0.1 netmask 255.0.0.0

[root@ubuntu2204 ~]# ifconfig | grep -E '([0-9]{1,3}.){3}[0-9]{1,3}'
	inet 10.0.0.206 netmask 255.255.255.0 broadcast 10.0.0.255
	RX packets 17025 bytes 3823233 (3.8 MB)
	TX packets 15223 bytes 3567976 (3.5 MB)
	inet 127.0.0.1 netmask 255.0.0.0

[root@ubuntu2204 ~]# ifconfig ens33 | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}'|head -1
10.0.0.206

[root@ubuntu2204 ~]# cat reg.txt
([0-9]{1,3}\.){3}[0-9]{1,3}

[root@ubuntu2204 ~]# ifconfig | grep -oEf reg.txt
10.0.0.206
255.255.255.0
10.0.0.255
127.0.0.1
255.0.0.0
[root@ubuntu2204 ~]# grep -E 'root|bash' /etc/passwd
root:x:0:0:root:/root:/bin/bash
mage:x:1000:1000:mage:/home/mage:/bin/bash
jose:x:1001:1001::/home/jose:/bin/bash
tom:x:1002:1002::/home/tom:/bin/bash
jerry:x:1003:1003::/home/jerry:/bin/bash


[root@ubuntu2204 ~]# grep -e 'root' -e 'bash' /etc/passwd
root:x:0:0:root:/root:/bin/bash
mage:x:1000:1000:mage:/home/mage:/bin/bash
jose:x:1001:1001::/home/jose:/bin/bash
tom:x:1002:1002::/home/tom:/bin/bash
jerry:x:1003:1003::/home/jerry:/bin/bash
[root@ubuntu2204 ~]# grep -w root /etc/passwd
root:x:0:0:root:/root:/bin/bash

[root@ubuntu2204 ~]# grep '\<root\>' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@centos8 ~]# grep "^\(.*\)\>.*\<\1$" /etc/passwd
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
bash:x:1008:1008::/home/bash:/bin/bash
nologin:x:1011:1011::/home/nologin:/sbin/nologin

[root@centos8 ~]# grep -E "^(.*)\>.*\<\1$" /etc/passwd
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
bash:x:1008:1008::/home/bash:/bin/bash
nologin:x:1011:1011::/home/nologin:/sbin/nologin

[root@centos8 ~]# egrep "^(.*)\>.*\<\1$" /etc/passwd
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
bash:x:1008:1008::/home/bash:/bin/bash
nologin:x:1011:1011::/home/nologin:/sbin/nologin
  • 范例: 过滤掉文件的注释(包括#号的行)和空行
[root@centos8 ~]#grep -Ev '^$|#' /etc/fstab
UUID=01f1068e-6937-4fb2-b64b-0d7d6b85ad08 / xfs defaults 0 0
UUID=cb21e5ce-edf6-4ed1-8df9-ba98520a68dc /boot xfs defaults 0 0
UUID=9ea3524a-7cff-49a0-951e-8429a30bd0a0 /data xfs defaults 0 0
UUID=42174d44-41aa-448b-88bc-fd36d6a49e39 swap swap defaults 0 0
  • 范例:面试题,算出所有人的年龄总和
[root@ubuntu2204 ~]# cat age.txt
xiaoming=20
xiaohong=18
xiaoqiang=22

[root@ubuntu2204 ~]# cut -d"=" -f2 age.txt|tr '\n' + | grep -Eo ".*[0-9]"|bc
60

[root@ubuntu2204 ~]# grep -Eo "[0-9]+" age.txt | tr '\n' + | grep -Eo ".*[0-9]"|bc
60

[root@ubuntu2204 ~]# grep -oE '[0-9]+' age.txt| paste -s -d+ | bc
60

7.4.2 文本处理三剑客之 sed

7.4.2.1 sed 工作原理
  • sed 即 Stream EDitor,和 vi 不同,sed 是行编辑器
    在这里插入图片描述

Sed是从文件或管道中读取一行,处理一行,输出一行;再读取一行,再处理一行,再输出一行,直到最后一行。

每当处理一行时,把当前处理的行存储在临时缓冲区 模式空间(Pattern Space) 中,接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。

一次处理一行的设计模式使得sed性能很高,sed在读取大文件时不会出现卡顿的现象。

如果使用vi命令打开几十M上百M的文件,明显会出现有卡顿的现象,这是因为vi命令打开文件是一次性将文件加载到内存,然后再打开。Sed就避免了这种情况,一行一行的处理,打开速度非常快,执行速度也很快。

7.4.2.2 sed 基本用法
  • 格式:
sed [OPTION]... {script-only-if-no-other-script} [input-file]...

grep [OPTIONS...] PATTERN [FILE...]
常用选项含义
-n|–quiet|–silent不输出模式空间内容到屏幕,即不自动打印
-e script|–expression=script多个script,or 的关系
-f script-file|–file=script-file从指定文件中读取编辑脚本
-i[SUFFIX]|–in-place[=SUFFIX]-i 直接修改文件,-i.bak 以.bak后缀备份原文件
-c|–copy配合i一起使用,保留原文件
-l N|–line-length=N指定每行长度,如果过长,就拆分成多行,要加 `l’
- -posix禁用GNU扩展
-E|-r|–regexp-extended扩展正则表达式
-s|–separate将多个文件视为独立文件,而不是单个连续的长文件流
-ir此组合不支持
-ri支持
-i -r支持
-ni此组合危险,会清空文件
  • script 格式:
'AddrCmd' #地址命令 在哪些行,执行什么操作
  • 地址格式:
#为空,则表示对全文进行处理

#单地址,指定行
N #具体行号
$ #最后一行
/pattern/ #能被匹配到的每一行

#范围地址
M,N #第M行到第NM,+N #第M行到第M+N3,+4 表示从第3行到第7/pattern1/,/pattern2/ #从第一个匹配行开始,到第二个匹配行中间的行
M,/pattern/ #行号开始,匹配结束
/pattern/,N #匹配开始,行号结束

#步长
1~2 #奇数行
2~2 #偶数行
命令含义
p打印当前模式空间内容,追加到默认输出之后
Ip忽略大小写输出
d删除模式空间匹配的行,并立即启用下一轮循环
a [\]text在指定行后面追加文本,支持使用\n实现多行追加
i [\]text在行前面插入文本
c [\]text替换行为单行或多行文本
w file保存模式匹配的行至指定文件
r file读取指定文件的文本至模式空间中匹配到的行后
=为模式空间中的行打印行号
!模式空间中匹配行取反处理
q结束或退出sed
  • 查找替代
s/pattern/replace/修饰符 #查找替换,支持使用其它分隔符,可以是其它形式:s@@@,s###

#修饰符
g 		#行内全局替换
p 		#显示替换成功的行
w file 	#将替换成功的行保存至文件中
I|i 	#忽略大小写

#后向引用
\1 		#第一个分组
\2 		#第二个分组
\N 		#第N个分组
& 		#所有搜索内容
  • 范例:
#等待标准输入,script为空,默认是直接输出
[root@ubuntu2204 ~]# sed ''
123
123

[root@ubuntu2204 ~]# sed '' -
123
123

#script为空,默认输出内容
[root@ubuntu2204 ~]# sed '' /etc/issue
Ubuntu 22.04 LTS \n \l

#script 中执行p命令,再加上默认输出,所有每行都显示了两次
[root@ubuntu2204 ~]# sed 'p' /etc/issue
Ubuntu 22.04 LTS \n \l
Ubuntu 22.04 LTS \n \l

#关闭默认输出,script 为空,则无任何输出
[root@ubuntu2204 ~]# sed -n '' /etc/issue
[root@ubuntu2204 ~]#

#用 -n 选项关闭默认输出,script 中执行p命令
[root@ubuntu2204 ~]# sed -n 'p' /etc/issue
Ubuntu 22.04 LTS \n \l

#输出第一行
[root@ubuntu2204 ~]# sed -n '1p' /etc/passwd
root:x:0:0:root:/root:/bin/bash

#输出最后一行
[root@ubuntu2204 ~]# sed -n '$p' /etc/passwd
jerry:x:1003:1003::/home/jerry:/bin/bash
[root@Rockey9-1 ~]# sed -n '1~5p' passwd
root:x:0:0:root:/root:/bin/bash
sync:x:5:0:sync:/sbin:/bin/sync
games:x:12:100:games:/usr/games:/sbin/nologin
polkitd:x:998:996:User for polkitd:/:/sbin/nologin
libstoragemgmt:x:990:990:daemon account for libstoragemgmt:/:/usr/sbin/nologin
cockpit-wsinstance:x:986:985:User for cockpit-ws instances:/nonexisting:/sbin/nologin
gdm:x:42:42::/var/lib/gdm:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
[root@Rockey9-1 ~]# sed -n '1~10p' passwd
root:x:0:0:root:/root:/bin/bash
games:x:12:100:games:/usr/games:/sbin/nologin
libstoragemgmt:x:990:990:daemon account for libstoragemgmt:/:/usr/sbin/nologin
gdm:x:42:42::/var/lib/gdm:/sbin/nologin
#正则匹配,输出包含root的行
[root@rocky86 ~]# sed -n '/root/p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

#正则匹配,输出以root开头的行
[root@rocky86 ~]# sed -n '/^root/p' /etc/passwd
root:x:0:0:root:/root:/bin/bash

#正则匹配,输出以bash结尾的行
[root@rocky86 ~]# sed -n '/bash$/p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
jose:x:1010:1010::/home/jose:/bin/bash
jerry:x:1011:1011::/home/jerry:/bin/bash

#正则匹配,显示注释行行号
[root@rocky86 0723]# sed -n '/^#/=' /etc/fstab
2
3
4

#行号开始,正则结束
[root@rocky86 0723]# sed -n '8,/root/p' /etc/passwd
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
  • 范例:添加内容
[root@ubuntu2204 ~]# cat test.txt
aaa
bbb
ccc
bbb

#匹配行后插入
[root@ubuntu2204 ~]# sed '/bbb/a\---' test.txt
aaa
bbb
---
ccc
bbb
---

#指定行前插入
[root@ubuntu2204 ~]# sed '2i\---' test.txt
aaa
---
bbb
ccc
bbb

[root@ubuntu2204 ~]# sed '2,4i\---' test.txt
aaa
---
bbb
---
ccc
---
bbb

#替换,第一行替换成 ---
[root@ubuntu2204 ~]# sed "1c\---" test.txt
---
bbb
ccc
bbb

#替换,第一行替换成两行
[root@ubuntu2204 ~]# sed "1c\---\n+++" test.txt
---
+++
bbb
ccc
bbb

#替换,多行替换成一行
[root@ubuntu2204 ~]# sed "1,2c\---" test.txt
---
ccc
bbb

# \ 的作用
[root@ubuntu2204 ~]# sed '2a *******' test.txt
aaa
bbb
*******
ccc
bbb

[root@ubuntu2204 ~]# sed '2a\ *******' test.txt
aaa
bbb
*******
ccc
bbb
  • 范例:
#取IP[root@ubuntu2204 ~]# ifconfig ens33 | head -n 2 | tail -n 1
	inet 10.0.0.206 netmask 255.255.255.0 broadcast 10.0.0.255

[root@ubuntu2204 ~]# ifconfig ens33 | sed -n '2p'
	inet 10.0.0.206 netmask 255.255.255.0 broadcast 10.0.0.255

[root@ubuntu2204 ~]# ifconfig ens33 | sed -n '/netmask/p'
	inet 10.0.0.206 netmask 255.255.255.0 broadcast 10.0.0.255
  • 范例:命令展开
[root@ubuntu2204 ~]# sed -n "/$(whoami)/p" /etc/passwd
root:x:0:0:root:/root:/bin/bash

#第1[root@ubuntu2204 ~]# sed -n "$[$(id -u)+1]p" /etc/passwd
root:x:0:0:root:/root:/bin/bash

[root@Rockey9-1 ~]# echo $[$(id -u)+1]p
1p

#倒数第2[root@ubuntu2204 ~]# sed -n "$(echo $[`cat /etc/passwd|wc -l`-1])p" /etc/passwd
jerry:x:1011:1011::/home/jerry:/bin/bash
  • 范例:变量展开
[root@ubuntu2204 ~]# sed -n "/$USER/p" /etc/passwd
root:x:0:0:root:/root:/bin/bash

[root@ubuntu2204 ~]# number=1;sed -n "${number}p" /etc/passwd
root:x:0:0:root:/root:/bin/bash
  • 范例:变量和命令
[root@ubuntu2204 ~]# sed -n "$(echo $UID+1|bc)p" /etc/passwd
root:x:0:0:root:/root:/bin/bash

[root@ubuntu2204 ~]# sed -n "$(echo $UID+1|bc),$(echo $UID+3|bc)p" /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
  • 范例:
[root@ubuntu2204 ~]# df | sed -n '/^\/dev\/sd/p'
/dev/sda2 1992552 255260 1616052 14% /boot

[root@Rockey9-1 ~]# df | sed -n '/\/dev/p'
devtmpfs                    4096       0      4096   0% /dev
tmpfs                     896416       0    896416   0% /dev/shm
/dev/mapper/rl_10-root  73364480 5643216  67721264   8% /
/dev/mapper/rl_10-home 133075472  965588 132109884   1% /home
/dev/sda1                1038336  298436    739900  29% /boot
[root@Rockey9-1 ~]# df | sed -n '/^\/dev/p'
/dev/mapper/rl_10-root  73364480 5643216  67721264   8% /
/dev/mapper/rl_10-home 133075472  965588 132109884   1% /home
/dev/sda1                1038336  298436    739900  29% /boot
[root@Rockey9-1 ~]# df | sed -n '/^\/dev\/sd/p'
/dev/sda1                1038336  298436    739900  29% /boot

[root@ubuntu2204 ~]# seq 10 | sed -n '2,4p'
2
3
4

[root@ubuntu2204 ~]# seq 10 | sed -n '2,+4p'
2
3
4
5

[root@ubuntu2204 ~]# seq 10 | sed -n '8,$p'
8
9
10

[root@ubuntu2204 ~]# seq 10 |sed -n '1~2p'
1
3
5
7
9

[root@ubuntu2204 ~]# seq 10 |sed -n '2~2p'
2
4
6
8
10


#删除奇数行
[root@ubuntu2204 ~]# seq 10 |sed '1~2d'
2
4
6
8
10

#删除偶数行
[root@ubuntu2204 ~]# seq 10 |sed '2~2d'
1
3
5
7
9

#或
[root@ubuntu2204 ~]# #sed -e '2d' -e '4d' seq.log
1
3
5
6
7
8
9
10

#多个script 写在一起
[root@ubuntu2204 ~]# sed '2d;4d' seq.log
1
3
5
6
7
8
9
10

#只显示非#开头的行
[root@ubuntu2204 ~]# sed -n '/^#/!p' /etc/fstab

[root@Rockey9-1 ~]# sed -n '/^[^#]/p' /etc/fstab 
/dev/mapper/rl_10-root  /                       xfs     defaults        0 0
UUID=f8abaa75-ba1c-462f-b7e1-06b73ffc3d1d /boot                   xfs     defaults        0 0
/dev/mapper/rl_10-home  /home                   xfs     defaults        0 0
/dev/mapper/rl_10-swap  none                    swap    defaults        0 0
[root@Rockey9-1 ~]# sed -n '/^#/!p' /etc/fstab 
# 此处为空行
/dev/mapper/rl_10-root  /                       xfs     defaults        0 0
UUID=f8abaa75-ba1c-462f-b7e1-06b73ffc3d1d /boot                   xfs     defaults        0 0
/dev/mapper/rl_10-home  /home                   xfs     defaults        0 0
/dev/mapper/rl_10-swap  none                    swap    defaults        0 0
  • 范例:
#不显示注释行和空行
# 删除#号开头的行和空行
[root@ubuntu2204 ~]# sed '/^$/d;/^#/d' fstab

[root@ubuntu2204 ~]# sed -En '/^(#|$)/!p' fstab

[root@ubuntu2204 ~]# grep -Ev '^#|^$' fstab
  • 范例:修改文件
#修改前备份
[root@ubuntu2204 ~]# seq 10 > 10.txt
[root@ubuntu2204 ~]# sed -i.bak '2,7d' 10.txt
[root@ubuntu2204 ~]# ll 10*
-rw-r--r-- 1 root root 9 Jul 23 19:02 10.txt
-rw-r--r-- 1 root root 21 Jul 23 19:01 10.txt.bak

[root@ubuntu2204 ~]# cat 10.txt
1
8
9
10

#不备份
[root@ubuntu2204 ~]# rm -f 10.txt.bak
[root@ubuntu2204 ~]# seq 10 > 10.txt
[root@ubuntu2204 ~]# sed -i '2,7d' 10.txt
[root@ubuntu2204 ~]# ll 10*
-rw-r--r-- 1 root root 9 Jul 23 19:05 10.txt

[root@ubuntu2204 ~]# cat 10.txt
1
8
9
10
  • 范例:删除注释行和空行
#备份文件
[root@ubuntu2204 ~]# sed -i.bak '/^#/d;/^$/d' fstab

[root@ubuntu2204 ~]# sed -i.bak '/^\(#\|$\)/d' fstab

[root@ubuntu2204 ~]# sed -Ei.bak '/^(#|$)/d' fstab
  • 范例: 搜索替换和&(引用)
# g【global】全局  p输出
[root@rocky86 0723]# sed -n 's/root/ROOT/gp' /etc/passwd
ROOT:x:0:0:ROOT:/ROOT:/bin/bash
operator:x:11:0:operator:/ROOT:/sbin/nologin

[root@rocky86 0723]# sed -n 's/root/&er/gp' /etc/passwd
rooter:x:0:0:rooter:/rooter:/bin/bash
operator:x:11:0:operator:/rooter:/sbin/nologin

[root@rocky86 0723]# sed -n 's/r..t/&er/gp' /etc/passwd
rooter:x:0:0:rooter:/rooter:/bin/bash
operator:x:11:0:operator:/rooter:/sbin/nologin
ftp:x:14:50:FTP User:/var/fterp:/sbin/nologin
clevis:x:994:988:Clevis Decrypterion Framework unprivileged
user:/var/cache/clevis:/sbin/nologin
[root@Rockey9-1 ~]# sed -i.bak 's/root/ROOT/g' passwd

[root@Rockey9-1 ~]# grep 'ROOT' passwd
ROOT:x:0:0:ROOT:/ROOT:/bin/bash
operator:x:11:0:operator:/ROOT:/sbin/nologin
[root@Rockey9-1 ~]# grep 'root' passwd.bak 
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
  • 范例: 除指定文件外其余删除
[root@ubuntu2204 ~]# ls
f-1.txt f-2.txt f-3.txt f-4.txt f-5.txt f-6.txt f-7.txt f-8.txt

#取非 1|3|5|7
[root@ubuntu2204 ~]# ls | grep -Ev 'f-(1|3|5|7)\.txt'
f-2.txt
f-4.txt
f-6.txt
f-8.txt

#删除非1|3|5|7
[root@ubuntu2204 ~]# rm -f `ls | grep -Ev 'f-(1|3|5|7)\.txt'`
[root@ubuntu2204 ~]# ls
f-1.txt f-3.txt f-5.txt f-7.txt


#取非 1|3|5|7
[root@ubuntu2204 ~]# ls |sed -n '/f-[^1357]\.txt/p'
f-2.txt
f-4.txt
f-6.txt
f-8.txt


#删除非1|3|5|7
[root@ubuntu2204 ~]# rm -f `ls |sed -n '/f-[^1357]\.txt/p'`
[root@ubuntu2204 ~]# ls
f-1.txt f-3.txt f-5.txt f-7.txt

[root@ubuntu2204 ~]# ls | grep -Ev 'f-(1|3|5|7)\.txt'
f-2.txt
f-4.txt
f-6.txt
f-8.txt

[root@ubuntu2204 ~]# ls | grep -Ev 'f-(1|3|5|7)\.txt' | sed -n 's/.*/rm &/p'
rm f-2.txt
rm f-4.txt
rm f-6.txt
rm f-8.txt

[root@ubuntu2204 ~]# ls | grep -Ev 'f-(1|3|5|7)\.txt' | sed -n 's/.*/rm &/p' | bash
[root@ubuntu2204 ~]# ls
f-1.txt f-3.txt f-5.txt f-7.txt

[root@ubuntu2204 ~]# ls | grep -Ev 'f-(1|3|5|7)\.txt' | sed -En 's/(.*)/rm \1/p' | bash
[root@ubuntu2204 ~]# ls
f-1.txt f-3.txt f-5.txt f-7.txt
  • 范例: 获取分区利用率
[root@ubuntu2204 ~]# df | sed -En '/^\/dev\/sd/s/.* ([0-9]+)%.*/\1/p'
14

[root@ubuntu2204 ~]# df | sed -En 's/^\/dev.* ([0-9]+)%.*/\1/p' | sort -nr
14
7
[root@Rockey9-1 test]# df | sed -n '/^\/dev/p'
/dev/mapper/rl_10-root  73364480 5665316  67699164   8% /
/dev/mapper/rl_10-home 133075472  965588 132109884   1% /home
/dev/sda1                1038336  298436    739900  29% /boot

[root@Rockey9-1 test]# df | sed -n '/^\/dev\/sd/p'
/dev/sda1                1038336  298436    739900  29% /boot

[root@Rockey9-1 test]# df | sed -En 's/^\/dev\/sd.* ([0-9]+)%/\1/p'
29 /boot

[root@Rockey9-1 test]# df | sed -En 's/^\/dev\/sd.* ([0-9]+)%.*/\1/p'
29
  • 范例:取IP 地址
[root@ubuntu2204 ~]# ifconfig ens33 |sed -nr "2s/[^0-9]+([0-9.]+).*/\1/p"
10.0.0.206

[root@ubuntu2204 ~]# ifconfig ens33 | sed -En '2s/^[^0-9]+([0-9.]{7,15}).*/\1/p'
10.0.0.206

[root@ubuntu2204 ~]# ifconfig ens33 | sed -rn '2s/^[^0-9]+([0-9.]+) .*$/\1/p'
10.0.0.206

[root@ubuntu2204 ~]# ifconfig ens33 | sed -n '2s/^.*inet //p' | sed -n 's/netmask.*//p'
10.0.0.206

[root@ubuntu2204 ~]# ifconfig ens33 | sed -n '2s/^.*inet //;s/ netmask.*//p'
10.0.0.206

[root@ubuntu2204 ~]# ifconfig ens33 | sed -rn '2s/(.*inet )([0-9].*)(netmask.*)/\2/p'
10.0.0.206

[root@Rockey9-1 test]# ifconfig eth0 | sed -En 's/.* inet (.*) netmask.*/\1/p'
10.0.0.153 
[root@Rockey9-1 ~]# ifconfig eth0 | sed -En 's/^[^0-9]*([0-9]{1,3}\.)/\1--/p'
10.--0.0.153  netmask 255.255.255.0  broadcast 10.0.0.255
[root@Rockey9-1 ~]# ifconfig eth0 | sed -En 's/^[^0-9]*(([0-9]{1,3}\.){3})/\1--/p'
10.0.0.--153  netmask 255.255.255.0  broadcast 10.0.0.255
[root@Rockey9-1 ~]# ifconfig eth0 | sed -En 's/^[^0-9]*(([0-9]{1,3}\.){3}[0-9]{1,3})/\1--/p'
10.0.0.153--  netmask 255.255.255.0  broadcast 10.0.0.255
[root@Rockey9-1 ~]# ifconfig eth0 | sed -En 's/^[^0-9]*(([0-9]{1,3}\.){3}[0-9]{1,3}).*/\1--/p'
10.0.0.153--
[root@Rockey9-1 ~]# ifconfig eth0 | sed -En 's/^[^0-9]*(([0-9]{1,3}\.){3}[0-9]{1,3}).*/\1/p'
10.0.0.153
  • 范例:取基名和目录名
#取基名
[root@rocky86 0723]# basename "/etc/sysconfig/network-scripts/"
network-scripts

[root@rocky86 0723]# echo "/etc/sysconfig/network-scripts/" | sed -nE 's#(^/.*/)([^/]+)/?#\2#p'
network-scripts

#取目录
[root@rocky86 0723]# dirname "/etc/sysconfig/network-scripts/"
/etc/sysconfig

[root@rocky86 0723]# echo "/etc/sysconfig/network-scripts/" | sed -nE 's#(^/.*)/([^/]+)/?#\1#p'
/etc/sysconfig
  • 范例: 取文件的前缀和后缀
[root@ubuntu2204 ~]# echo a.txt | sed -En 's/(.*)\.([^.]+)$/\1 \2/p'
a txt

[root@ubuntu2204 ~]# echo a.tar.gz.txt | sed -En 's/(.*)\.([^.]+)$/\1 \2/p'
a.tar.gz txt
  • 范例:将非#开头的行加#
[root@rocky86 0723]# sed -rn ‘s/^[^#]/#&/p’ fstab
#/dev/mapper/rl-root / xfs defaults 0 0
#UUID=94a73757-555a-4d2b-8153-f54277c4c50d /boot xfs defaults 0 0
#/dev/mapper/rl-home /home xfs defaults 0 0
#/dev/mapper/rl-swap none swap defaults 0 0

[root@rocky86 0723]# sed -rn 's/^[^#](.*)/#\1/p' fstab
#/dev/mapper/rl-root / xfs defaults 0 0
#UUID=94a73757-555a-4d2b-8153-f54277c4c50d /boot xfs defaults 0 0
#/dev/mapper/rl-home /home xfs defaults 0 0
#/dev/mapper/rl-swap none swap defaults 0 0

[root@rocky86 0723]# sed -rn '/^#/!s@^@#@p' fstab
#
#/dev/mapper/rl-root / xfs defaults 0 0
#UUID=94a73757-555a-4d2b-8153-f54277c4c50d /boot xfs defaults 0 0
#/dev/mapper/rl-home /home xfs defaults 0 0
#/dev/mapper/rl-swap none swap defaults 0 0
  • 范例:将#开头的行删除#
[root@rocky86 0723]# sed -ri.bak '/^#/s/^#//' fstab

[root@rocky86 0724]# sed -E -i.bak 's/^#(.*)/\1/' fstab
[root@Rockey9-1 ~]# sed -En '/^#/p' fstab 
#
# /etc/fstab
# Created by anaconda on Tue Apr 16 10:24:12 2024
#
# Accessible filesystems, by reference, are maintained under '/dev/disk/'.
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info.
#
# After editing this file, run 'systemctl daemon-reload' to update systemd
# units generated from this file.
#
[root@Rockey9-1 ~]# sed -En '/^#/s/^#//p' fstab 

 /etc/fstab
 Created by anaconda on Tue Apr 16 10:24:12 2024

 Accessible filesystems, by reference, are maintained under '/dev/disk/'.
 See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info.

 After editing this file, run 'systemctl daemon-reload' to update systemd
 units generated from this file.

[root@Rockey9-1 ~]# sed -Ei.bak '/^#/s/^#//' fstab 
[root@Rockey9-1 ~]# cat fstab


 /etc/fstab
 Created by anaconda on Tue Apr 16 10:24:12 2024

 Accessible filesystems, by reference, are maintained under '/dev/disk/'.
 See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info.

 After editing this file, run 'systemctl daemon-reload' to update systemd
 units generated from this file.

/dev/mapper/rl_10-root  /                       xfs     defaults        0 0
UUID=f8abaa75-ba1c-462f-b7e1-06b73ffc3d1d /boot                   xfs     defaults        0 0
/dev/mapper/rl_10-home  /home                   xfs     defaults        0 0
/dev/mapper/rl_10-swap  none                    swap    defaults        0 0
  • 范例:取分区利用率
[root@rocky86 0723]# df | sed -nr '/^\/dev/s# .* ([0-9]+)%.*# \1#p'
/dev/mapper/rl-root 16
/dev/mapper/rl-home 1
/dev/sda1 26

[root@rocky86 0723]# df | sed -rn '/^\/dev/ s#([^[:space:]]+[[:space:]]+){4}(.*)%.*#\2#p'
16
1
26

[root@rocky86 0723]# df | sed -rn '/^\/dev/ s#(\S+\s+){4}(.*)%.*#\2#p'
16
1
26
ens160,ens192...

批量管理
100台机 10 ens160 20 ens192....

eth0
  • 范例:修改网卡名称
#centos7,8
[root@rocky86 0723]# sed -Ei.bak 's/^(GRUB_CMDLINE_LINUX=.*)"$/\1
net.ifnames=0"/' /etc/default/grub

[root@rocky86 0723]# sed -Ei '/^GRUB_CMDLINE_LINUX/s#"$# net.ifnames=0"#'
/etc/default/grub

#修改完成后重启生效
[root@rocky86 0723]# grub2-mkconfig -o /etc/grub2.cfg;reboot;

#ubuntu2204
[root@ubuntu2204 ~]# sed -Ei '/^GRUB_CMDLINE_LINUX/s#"$# net.ifnames=0"#'
/etc/default/grub

[root@ubuntu2204 ~]# grub-mkconfig -o /boot/grub/grub.cfg;reboot
  • 范例:修改selinux配置
[root@rocky86 0723]# cp /etc/selinux/config ./

[root@rocky86 0723]# sed -i.bak '/SELINUX=enforcing/c SELINUX=disabled' config

[root@rocky86 0723]# sed -i.bak '/^SELINUX=/c SELINUX=disabled/' config

[root@rocky86 0723]# sed -Ei.bak 's/^SELINUX=.*/SELINUX=disabled/' config

[root@rocky86 0723]# sed -Ei.bak 's/^(SELINUX=)(.*)/\1disabled/' config
  • 范例: 显示前4行
[root@ubuntu2204 ~]# seq 10 > test.txt

[root@ubuntu2204 ~]# sed 4q test.txt
1
2
3
4
7.4.2.3 sed 高级用法

sed 中除了模式空间,还另外还支持保持空间(Hold Space),利用此空间,可以将模式空间中的数据,临时保存至保持空间,从而后续接着处理,实现更为强大的功能。
在这里插入图片描述

常见的高级命令含义
P打印模式空间开端至\n内容,并追加到默认输出之前
h把模式空间中的内容覆盖至保持空间中
H把模式空间中的内容追加至保持空间中
g从保持空间取出数据覆盖至模式空间
G从保持空间取出内容追加至模式空间
x把模式空间中的内容与保持空间中的内容进行互换
n读取匹配到的行的下一行覆盖至模式空间
N读取匹配到的行的下一行追加至模式空间
d删除模式空间中的行
D如果模式空间包含换行符,则删除直到第一个换行符的模式空间中的文本,并不会读取新的输入行,而使用合成的模式空间重新启动循环。如果模式空间不包含换行符,则会像发出d命令那样启动正常的新循环
  • 范例:
sed -n 'n;p' FILE
seq 10 | sed 'N;s/\n//'
sed '1!G;h;$!d' FILE
seq 10 | sed -n '/3/{g;1!p;};h' 	#前一行
seq 10 | sed -nr '/3/{n;p}' 		#后一行
sed 'N;D' FILE
seq 10 |sed '3h;9G;9!d'
sed '$!N;$!D' FILE
sed '$!d' FILE
sed 'G' FILE
sed 'g' FILE
sed '/^$/d;G' FILE
sed 'n;d' FILE
sed -n '1!G;h;$p' FILE
  • 范例: 打印偶数行
[root@ubuntu2204 ~]# seq 10 | sed -n 'n;p'
2
4
6
8
10

[root@ubuntu2204 ~]# seq 10 | sed -n '2~2p'
2
4
6
8
10

[root@ubuntu2204 ~]# seq 10 | sed '1~2d'
2
4
6
8
10

[root@ubuntu2204 ~]# seq 10 | sed -n '1~2!p'
2
4
6
8
10

7.4.3 文本处理三剑客之 awk

7.4.3.1 awk 工作原理和基本用法说明

awk:Aho, Weinberger, Kernighan,报告生成器,格式化文本输出,GNU/Linux发布的AWK目前由自由软件基金会(FSF)进行开发和维护,通常也称它为 GNU AWK

  • AWK有多种版本:
    • AWK:原先来源于 AT & T 实验室的的AWK
    • NAWK:New awk,AT & T 实验室的AWK的升级版
    • GAWK:即GNU AWK。所有的GNU/Linux发布版都自带GAWK,它与AWK和NAWK完全兼容
  • 目前主流发行版LINUX中使用的都是GAWK
[root@rocky86 ~]# ll `which awk`
lrwxrwxrwx. 1 root root 4 Apr 16 05:17 /usr/bin/awk -> gawk
[root@ubuntu2204 ~]# which awk
/usr/bin/awk
[root@ubuntu2204 ~]# ll /usr/bin/awk
lrwxrwxrwx 1 root root 21 Apr 21 2022 /usr/bin/awk -> /etc/alternatives/awk*
[root@ubuntu2204 ~]# ll /etc/alternatives/awk
lrwxrwxrwx 1 root root 13 Apr 21 2022 /etc/alternatives/awk -> /usr/bin/gawk*
awk [options] 'program' var=value file…
awk [options] -f programfile var=value file…
#常用选项
-f progfile|--file progfile #从文件中读入program
-F fs|--field-separator fs 	#指定分隔符,默认是空白符,可以指定多个
-v var=val|--asign var=val 	#设置变量
  • program用法
program 通常放在单引号中,由三部份组成,分别是BEGIN{}[pattern]{COMMAND}END{},这三部份可以没有或都有

awk '' /etc/issue

awk 'BEGIN{print "begin"}' /etc/issue
awk '{print $0}' /etc/issue
awk 'END{print "end"}' /etc/issue


awk 'BEGIN{print "begin"}{print $0}END{print "end"}' /etc/issue
  • Program格式:
pattern{action statements;..}

pattern #定义条件,只有符合条件的记录,才会执行后面的
action
action statements #处理数据的方式,常见如 print,printf等
  • awk 工作过程
  1. 执行BEGIN{action;… }语句块中的语句
  2. 从文件或标准输入(stdin)读取一行,然后执行pattern{ action;… }语句块,它逐行扫描文件,从第一行到最后一行重复这个过程,直到文件全部被读取完毕。
  3. 当读至输入流末尾时,执行END{action;…}语句块
    在这里插入图片描述

BEGIN语句块在awk开始从输入流中读取行之前被执行,这是一个可选的语句块,比如变量初始化、打印输出表格的表头等语句通常可以写在BEGIN语句块中

END语句块在awk从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在END语句块中完成,它也是一个可选语句块

pattern语句块中的通用命令是最重要的部分,也是可选的。如果没有提供pattern语句块,则默认执行{print },即打印每一个读取到的行,awk读取的每一行都会执行该语句块

  • 分割符、域和记录
    • 文件的每一行称为记录 record
    • 记录可以由指定的分隔符分隔成多个字段(列 column,域 field),由$1, 2... 2... 2...N 来标识每一个字段,$0 表示一整行
7.4.3.2 awk 变量

awk中的变量分为内置和自定义变量两种

变量的作用在于可以一次定义多次调用

7.4.3.2.1 内置变量
  • 常用内置变量
变量名含义
FILENAME当前文件名
FS字段分隔符,默认为空白字符,功能相当于 -F
RS换行符,用于分割指定文件的行,默认是换行符
OFS输出字段分隔符,默认为空白字符
ORS输出换行符,输出时用指定符号代替换行符
OFMT数字的输出格式,默认值是%.6g
NF一条记录的字段数量
NR已经读出的记录数,就是行号,从1开始
FNR各文件分别记录行号
ARGC命令行参数的个数
ARGV数组,保存的是命令行所给定的各参数,每一个参数:ARGV[0],… ,ARGV[n]
  • 范例:FILENAME
#begin中拿不到FILENAME
[root@ubuntu2204 ~]# awk 'BEGIN{print FILENAME}' /etc/issue

[root@ubuntu2204 ~]# awk 'BEGIN{print FILENAME}{print "test"}END{print FILENAME}' /etc/issue
test
test
/etc/issue
  • 范例:FS
[root@ubuntu2204 ~]# awk -v FS=":" 'BEGIN{print FS}{print $1,$1}' /etc/passwd
:
root root
daemon daemon

[root@ubuntu2204 ~]# awk -v FS=":" 'BEGIN{print FS}{print $1FS$1}' /etc/passwd
:
root:root
daemon:daemon

#使用-F选项指定
[root@ubuntu2204 ~]# awk -F: 'BEGIN{print FS}{print $1FS$1}' /etc/passwd
:
root:root
daemon:daemon

#从shell变量中获取
[root@ubuntu2204 ~]# str=":";awk -v FS=$str 'BEGIN{print FS}{print $1FS$1}' /etc/passwd
:
root:root
daemon:daemon

#-FFS变量功能一样,同时使用后面会覆盖前面的
[root@ubuntu2204 ~]# awk -v FS=":" -F";" 'BEGIN{print FS}' /etc/passwd
;

[root@ubuntu2204 ~]# awk -F";" -v FS=":" 'BEGIN{print FS}' /etc/passwd
:
  • 范例:同时使用多个分隔符
[root@ubuntu2204 ~]# df -h | awk '{print $5}' | tr -d "%" | sort -nr | head -1
14
[root@ubuntu2204 ~]# df -h | awk -F" +|%" '{print $5}' | sort -nr | head -1
14
  • 范例:RS
[root@ubuntu2204 ~]# cat test.txt -A
a b c;1 2 3;x y z$
[root@ubuntu2204 ~]# awk '{print $0}' test.txt
a b c;1 2 3;x y z

[root@ubuntu2204 ~]# awk -v RS=";" '{print $0}' test.txt
a b c
1 2 3
x y z
  • 范例:OFS
[root@ubuntu2204 ~]# awk -v FS=":" '{print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3

[root@ubuntu2204 ~]# awk -v FS=":" -v OFS="---" '{print $1,$3}' /etc/passwd
root---0
bin---1
daemon---2
adm---3
  • 范例:ORS
[root@ubuntu2204 ~]# cat -A test.txt
123$
456$
789$

[root@ubuntu2204 ~]# awk '{print $0}' test.txt
123
456
789

[root@ubuntu2204 ~]# awk -v ORS="---" '{print $0}' test.txt
123---456---789---[root@rocky86 ~]#
  • 范例:OFMT
[root@ubuntu2204 ~]# awk 'BEGIN{PI=3.1415926;print PI;OFMT="%.1g";print
PI;OFMT="%.2g";print PI;OFMT="%.8g";print PI;OFMT="%.8f";print PI}'
3.14159
3
3.1
3.1415926
3.14159260
  • 范例:NF
[root@ubuntu2204 ~]# awk -v FS=":" '{print NF}' /etc/passwd
7
7
7

[root@ubuntu2204 ~]# awk -v FS=":" '{print $NF}' /etc/passwd
/bin/bash
/sbin/nologin
/sbin/nologin

[root@ubuntu2204 ~]# awk -v FS=":" '{print $(NF-1)}' /etc/passwd
/root
/bin
/sbin
  • 范例:NR
[root@ubuntu2204 ~]# awk '{print NR,$0}' /etc/issue
1 Ubuntu 22.04 LTS \n \l
2
  • 范例:FNR
[root@ubuntu2204 ~]# awk '{print NR,FNR,$0}' /etc/issue /etc/os-release
1 1 Ubuntu 22.04 LTS \n \l
2 2
3 1 PRETTY_NAME="Ubuntu 22.04 LTS"
4 2 NAME="Ubuntu"
5 3 VERSION_ID="22.04"
6 4 VERSION="22.04 (Jammy Jellyfish)"
7 5 VERSION_CODENAME=jammy
8 6 ID=ubuntu
9 7 ID_LIKE=debian
10 8 HOME_URL="https://www.ubuntu.com/"
11 9 SUPPORT_URL="https://help.ubuntu.com/"
12 10 BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
13 11 PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-andpolicies/
privacy-policy"
14 12 UBUNTU_CODENAME=jammy
  • 范例:ARGC
[root@ubuntu2204 ~]# awk 'BEGIN{print ARGC}'
1
[root@ubuntu2204 ~]# awk 'BEGIN{print ARGC}' /etc/issue
2
[root@ubuntu2204 ~]# awk 'BEGIN{print ARGC}' /etc/issue /etc/os-relaease
3
  • 范例:ARGV
[root@ubuntu2204 ~]# awk 'BEGIN{print ARGC,"---",ARGV[0],"---",ARGV[1],"---",ARGV[2],"---",ARGV[3]}' /etc/issue /etc/os-relaease
3 --- awk --- /etc/issue --- /etc/os-relaease ---
7.4.3.2.2 自定义变量
  • AWK中的自定义变量命名要区分大小写
  • 用 -v 选项定义自定义变量
[root@ubuntu2204 ~]# awk -v var1=abc 'BEGIN{print var1}'
abc

[root@ubuntu2204 ~]# awk -v var1=abc -v VAR1=123 'BEGIN{print var1;print VAR1}'
abc
123
  • 在program中定义
[root@ubuntu2204 ~]# awk -v var1=abc 'BEGIN{print var1;var1=def; print var1}'
abc

#定义的时候要加双引号
[root@rocky86 ~]# awk -v var1=abc 'BEGIN{print var1;var1="def"; print var1}{print var1}' /etc/redhat-release
abc
def
def
7.4.3.3 动作 print
  • 格式:
print item1,item2, ...
  • 说明:
    • 逗号分隔符
    • 输出item可以字符串,也可是数值;当前记录的字段、变量或awk的表达式
    • 如省略item,相当于print $0
    • 固定字符串需要用“ ” 引起来,而变量和数字不需要
  • 范例:
[root@ubuntu2204 ~]# awk 'BEGIN{print "hello world"}'
hello world

[root@ubuntu2204 ~]# seq 3 | awk '{print "hello world"}'
hello world
hello world
hello world

[root@ubuntu2204 ~]# seq 3 | awk '{print $0}'
1
2
3

[root@ubuntu2204 ~]# awk 'BEGIN{print 2*3}'
6

[root@ubuntu2204 ~]# awk '{print }' /etc/issue
Ubuntu 22.04 LTS \n \l

[root@ubuntu2204 ~]# awk -v var1=123 -F: '{print var1,$1,$3}' /etc/passwd
123 root 0
123 daemon 1
123 bin 2
123 sys 3

[root@ubuntu2204 ~]# awk -v var1=123 -F: '{print var1,$1"\t"$3}' /etc/passwd
123 root 0
123 daemon 1
123 bin 2
123 sys 3
123 sync 4
7.4.3.4 动作 printf
  • printf 可以实现格式化输出
  • 格式:
printf "FORMAT",item1,item2,...
  • 说明:
    • 必须指定FORMAT
    • 不会自动换行,需要显式给出换行控制符 \n
    • FORMAT中需要分别为后面每个item指定格式符
  • 格式符:与item一一对应
格式符含义
%s显示字符串
%d|%i显示十进制整数
%f显示为浮点数
%e|%E显示科学计数法数值
%c显示字符的ASCII码
%g|%G以科学计数法或浮点形式显示数值
%u无符号整数
%%显示%自身
  • 修饰符
修饰符含义
M[.N]M表示显示的宽度;N表示小数点后精度,如%3.1f
-左对齐(默认右对齐) 如 %-15s
+显示数值的正负符号 如 %+d
  • 范例:
[root@ubuntu2204 ~]$ awk -F: '{printf "%s",$1}' /etc/passwd
rootdaemonbinsyssyncgamesmanlpmailnewsuucpproxywww-databackuplistircgnatsnobody_aptsystemd-networksystemd-resolvemessagebussystemd-timesyncpollinatesshdsysloguuiddtcpdumptsslandscapeusbmuxdangelxdmagetomuser1jose[root@ubuntu2204 ~]$

范例:

[root@ubuntu2204 ~]# awk -F: '{printf "%s\n",$1}' /etc/passwd
root
daemon
bin

[root@ubuntu2204 ~]# awk -F: '{printf "%20s\n",$1}' /etc/passwd
		root
		daemon
		bin
		sys
		sync

[root@ubuntu2204 ~]# awk -F: '{printf "%-20s %10d\n",$1,$3}' /etc/passwd
root 	0
daemon 	1
bin 	2
sys 	3

[root@ubuntu2204 ~]# awk -F: '{printf "Username: %s\n",$1}' /etc/passwd
Username: root
Username: daemon
Username: bin
Username: sys

[root@ubuntu2204 ~]# awk -F: '{printf "Username: %25sUID:%d\n",$1,$3}' /etc/passwd
Username: 	  rootUID:0
Username: 	daemonUID:1
Username: 	   binUID:2

[root@ubuntu2204 ~]# awk -F: '{printf "Username: %-25sUID:%d\n",$1,$3}' /etc/passwd
Username: root 		UID:0
Username: daemon 	UID:1
Username: bin 		UID:2
[root@ubuntu2204 ~]# awk -F: 'BEGIN{print "---------------------------
\n|username |uid |\n---------------------------"}{printf
"|%-20s|%5s|\n---------------------------\n",$1,$3}' /etc/passwd
---------------------------
|username |uid |
---------------------------
|root | 0|
---------------------------
|daemon | 1|
---------------------------
|bin | 2|
---------------------------
7.4.3.5 操作符
  • 赋值操作
=, +=, -=, *=, /=, %=, ^=, ++, --
  • 比较操作
==, !=, >, >=, <, <=
  • 算术运算符
x+y, x-y, x*y, x/y, x^y, x%y
-x 		#转换为负数
+x 		#将字符串转换为数值
  • 范例:
[root@ubuntu2204 ~]# awk 'BEGIN{i=0;print i++,i}'
0 1
[root@ubuntu2204 ~]# awk 'BEGIN{i=0;print ++i,i}'
1 1
[root@ubuntu2204 ~]# awk 'BEGIN{i=0;print ++i,++i}'
1 2
[root@ubuntu2204 ~]# awk 'BEGIN{i=0;print ++i,i++}'
1 1
  • 范例:
#默认print,先匹配,再++
[root@ubuntu2204 ~]# seq 3 | awk 'n++'
2
3

[root@ubuntu2204 ~]# awk -v n=0 '!n++' /etc/passwd
root:x:0:0:root:/root:/bin/bash

[root@ubuntu2204 ~]# awk -v n=0 '!n++{print n}' /etc/passwd
1

[root@ubuntu2204 ~]# awk -v n=1 '!n++{print n}' /etc/passwd

[root@ubuntu2204 ~]# awk -v n=0 '!++n{print n}' /etc/passwd

[root@ubuntu2204 ~]# awk -v n=0 '!++n' /etc/passwd

[root@ubuntu2204 ~]# awk -v n=-1 '!++n' /etc/passwd
root:x:0:0:root:/root:/bin/bash
  • 范例:
[root@ubuntu2204 ~]# awk 'NR==2' /etc/passwd
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

[root@ubuntu2204 ~]# awk -F: '$3>=1000' /etc/passwd
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
mage:x:1000:1000:mage:/home/mage:/bin/bash
jose:x:1001:1001::/home/jose:/bin/bash
tom:x:1002:1002::/home/tom:/bin/bash
jerry:x:1003:1003::/home/jerry:/bin/bash
  • 范例:取奇,偶数行
[root@ubuntu2204 ~]#seq 6 | awk 'NR%2==0'
2
4
6

[root@ubuntu2204 ~]# seq 6 | awk 'NR%2==1'
1
3
5

[root@ubuntu2204 ~]# seq 6 | awk 'NR%2!=0'
1
3
5

[root@ubuntu2204 ~]# seq 6 | awk 'NR%2!=1'
2
4
6
  • 模式匹配操作
~ 		#左边是否和右边匹配,包含关系
!~ 		#是否不匹配
  • 范例:
#包含root的行
[root@ubuntu2204 ~]# awk -F: '$0 ~ /root/{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

[root@ubuntu2204 ~]# awk '/root/' /etc/passwd
root:x:0:0:root:/root:/bin/bash


#以root开头的行
[root@ubuntu2204 ~]# awk -F: '$0 ~ "^root"{print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

[root@ubuntu2204 ~]# awk '/^root/' /etc/passwd
root:x:0:0:root:/root:/bin/bash


#不包含root的行
[root@ubuntu2204 ~]# awk '$0 !~ /root/' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

#UID0的行
[root@ubuntu2204 ~]# awk -F: '$3==0' /etc/passwd
root:x:0:0:root:/root:/bin/bash

[root@ubuntu2204 ~]# awk -F: '$3>1000' /etc/passwd
......
  • 范例:
[root@ubuntu2204 ~]# df | awk -F"[[:space:]]+|%" '$0 ~ /^\/dev\/sd/{print $5}'
14

[root@ubuntu2204 ~]# ifconfig eth0 | awk 'NR==2{print $2}'
10.0.0.150
  • 逻辑操作符
&& 		#与,并且关系
|| 		#或,或者关系
! 		#非,取反
  • 范例:!取反
[root@ubuntu2204 ~]# awk 'BEGIN{print i}'

[root@ubuntu2204 ~]# awk 'BEGIN{print !i}'
1
[root@ubuntu2204 ~]# awk -v i=10 'BEGIN{print !i}'
0
[root@ubuntu2204 ~]# awk -v i=0 'BEGIN{print !i}'
1
[root@ubuntu2204 ~]# awk -v i=-10 'BEGIN{print !i}'
0
[root@ubuntu2204 ~]#awk -v i=abc 'BEGIN{print !i}'
0
[root@ubuntu2204 ~]# awk -v i='' 'BEGIN{print !i}'
1
  • 范例:
[root@ubuntu2204 ~]# awk -F: '$3>=0 && $3<5 {print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4

[root@ubuntu2204 ~]# awk -F: '$3<1 || $3>1010 {print $1,$3}' /etc/passwd
root 0
nobody 65534
jerry 1011
tom 1012
tom-cat 1013

[root@ubuntu2204 ~]# awk -F: '!($3!=0) {print $1,$3}' /etc/passwd
root 0

[root@ubuntu2204 ~]# awk -F: '!($3>=5) {print $1,$3}' /etc/passwd
root 0
bin 1
daemon 2
adm 3
lp 4
  • 三目运算(条件表达式)
selector?if-true-expression:if-false-expression
  • 范例:
[root@ubuntu2204 ~]# awk -F: '{$3>=1000?utype="Common":utype="Sys"; printf "%-20s:%12s\n",$1,utype}' /etc/passwd
root : Sys
bin : Sys
daemon : Sys
adm : Sys

[root@ubuntu2204 ~]# df|awk -F"[ %]+" '/^\/dev\/sd/{$(NF-1)>10? disk="full":disk="OK";print $(NF-1),disk}'
26 full
3 OK
1 OK
13 full
  • 范例:
[root@ubuntu2204 ~]# cat scores.txt
wang 100
li 90
zhang 50
zhao 80
han 70

[root@ubuntu2204 ~]# awk '$2>=60?type="pass":type="nopass"{print $1,type}'
scores.txt
wang pass
li pass
zhang nopass
zhao pass
han pass
7.4.3.6 模式PATTERN
  • PATTERN:根据pattern条件,过滤匹配的行,再做处理,如果没有指定,则匹配每一行
  • 范例:
[root@ubuntu2204 ~]# awk '{print $0}' /etc/issue
Ubuntu 22.04 LTS \n \l

[root@ubuntu2204 ~]# awk 'NR==1{print $0}' /etc/issue
Ubuntu 22.04 LTS \n \l
  • 正则匹配
  • 范例:用正则匹配,正则表达式需要用/ / 来锚定开始结束
[root@rocky86 ~]# awk '/^UUID/{print $1}' /etc/fstab
UUID=94a73757-555a-4d2b-8153-f54277c4c50d

#非空行,非注释行
[root@rocky86 ~]# awk '!/^$|^#/{print $1}' /etc/fstab
/dev/mapper/rl-root
UUID=94a73757-555a-4d2b-8153-f54277c4c50d
/dev/mapper/rl-home
/dev/mapper/rl-swap
  • 关系表达式
    • 关系表达式,结果为“真”才会被处理
    • 真:结果为非0值,非空字符串
    • 假:结果为0值或空字符串
  • 范例:
[root@ubuntu2204 ~]# seq 3 | awk '1'
1
2
3
[root@ubuntu2204 ~]# seq 3 | awk '!1'
[root@ubuntu2204 ~]# seq 3 | awk '0'
[root@ubuntu2204 ~]# seq 3 | awk '!0'
1
2
3


#false 这里表示的是一个未定义的变量,不是表示bool值
[root@ubuntu2204 ~]# seq 3 | awk 'false'
[root@ubuntu2204 ~]# seq 3 | awk -v false=123 'false'
1
2
3


#false 这里表示的是字符串
[root@ubuntu2204 ~]# seq 3 | awk '"false"'
1
2
3

[root@ubuntu2204 ~]# seq 3 | awk '""'
[root@ubuntu2204 ~]# seq 3 | awk '" "'
1
2
3

[root@ubuntu2204 ~]# seq 3 | awk '"0"'
1
2
3
[root@ubuntu2204 ~]# seq 3 | awk '0'


#这里true 是变量名,一个不存在的变量
[root@ubuntu2204 ~]# seq 3 | awk 'true'
[root@ubuntu2204 ~]# seq 3 | awk -v true=123 'true'
1
2
3

[root@ubuntu2204 ~]# seq 3 | awk '123'
1
2
3
[root@ubuntu2204 ~]# seq 3 | awk 'mage'
[root@ubuntu2204 ~]# seq 3 | awk -v mage="mage" 'mage'
1
2
3
[root@ubuntu2204 ~]# seq 3 | awk -v mage="" 'mage'
[root@ubuntu2204 ~]# seq 3 | awk -v mage=" " 'mage'
1
2
3
[root@ubuntu2204 ~]# seq 3 | awk -v mage=0 'mage'

-范例:

[root@ubuntu2204 ~]# seq 3 | awk 'i'

#赋值
[root@ubuntu2204 ~]# seq 3 | awk 'i=0'

#比较
[root@ubuntu2204 ~]# seq 3 | awk 'i==0'
1
2
3

#赋值
[root@ubuntu2204 ~]# seq 3 | awk 'i=1'
1
2
3

#比较
[root@ubuntu2204 ~]#seq 3 | awk 'i==1'

#比较
[root@ubuntu2204 ~]# seq 3 | awk 'i==i'
1
2
3

#比较
[root@ubuntu2204 ~]# seq 3 | awk 'i!=i'

#赋值 i=false
[root@ubuntu2204 ~]# seq 3 | awk 'i=i'

#赋值 tru false true
[root@ubuntu2204 ~]# seq 3 | awk 'i=!i'
1
3

[root@ubuntu2204 ~]# seq 4 | awk '{i=!i;print i}'
1
0
1
0

[root@ubuntu2204 ~]# seq 8 | awk '!(i=!i)'
2
4
6
8

[root@ubuntu2204 ~]# seq 8 | awk -v i=1 'i=!i'
2
4
6
8
  • 范例:
[root@ubuntu2204 ~]# cat test.txt
1111111
2222222
aaaaaaa
bbbbbbb


[root@ubuntu2204 ~]# awk 'i=1;j=1{print i,j}' test.txt
1111111
1 1
2222222
1 1
aaaaaaa
1 1
bbbbbbb
1 1

#同上
[root@ubuntu2204 ~]# awk 'i=1{print $0};j=1{print i,j}' test.txt


[root@ubuntu2204 ~]# awk 'i=1,j=1{print i,j}' test.txt
1 1
1 1
1 1
1 1
  • 范例:
[root@ubuntu2204 ~]# awk -F: '$3>=1000{print $1,$3}' /etc/passwd
nobody 65534
mage 1000
jose 1001
tom 1002
jerry 1003

[root@ubuntu2204 ~]# awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd
root /bin/bash
mage /bin/bash
jose /bin/bash
tom /bin/bash
jerry /bin/bash
  • 行范围
    • 不支持直接用行号,但可以使用变量NR间接指定行号
    • 也可以用正则锚定开始结束行
  • 范例:
[root@ubuntu2204 ~]# seq 10 | awk 'NR>=3 && NR<6'
3
4
5

[root@ubuntu2204 ~]# awk 'NR>=3 && NR<6{print NR,$0}' /etc/passwd
3 bin:x:2:2:bin:/bin:/usr/sbin/nologin
4 sys:x:3:3:sys:/dev:/usr/sbin/nologin
5 sync:x:4:65534:sync:/bin:/bin/sync

#sed 写法
[root@ubuntu2204 ~]# sed -n '3,5p' /etc/passwd
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync

#正则锚定
[root@ubuntu2204 ~]# awk '/^bin/,/^adm/' /etc/passwd
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin

#sed写法
[root@ubuntu2204 ~]# sed -n '/^bin/,/^adm/p' /etc/passwd
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
  • BEGIN/END模式
    • BEGIN{}:仅在开始处理文件中的文本之前执行一次
    • END{}:仅在文本处理完成之后执行一次
#只有begin 时,可以没有输入内容
[root@ubuntu2204 ~]# awk 'BEGIN{print "begin"}'
begin

#需要后续输入
[root@ubuntu2204 ~]# awk 'BEGIN{print "begin"}{}'
begin

[root@ubuntu2204 ~]# awk 'BEGIN{print "begin"}{print $0}END{print "end"}'
/etc/issue
begin
Ubuntu 22.04 LTS \n \l

end
  • 范例:
[root@ubuntu2204 ~]# awk -F: 'BEGIN{printf "--------------------------------
\n%-20s|%10s|\n--------------------------------\n","username","uid"}{printf
"%-20s|%10d|\n--------------------------------\n",$1,$3}' /etc/passwd
--------------------------------
username | uid|
--------------------------------
root | 0|
--------------------------------
daemon | 1|
--------------------------------
bin | 2|
--------------------------------
7.4.3.7 条件判断 if-else
  • 使用场景:对awk取得的整行或某个字段做条件判断
  • 语法:
if(condition){statement;}[else statement]
if(condition1){statement1}else if(condition2){statement2}else if(condition3)
{statement3}...... else {statementN}
  • 范例:
[root@ubuntu2204 ~]# awk -F: '{if($3>=1000)print $1,$3}' /etc/passwd
nobody 65534
mage 1000
jose 1001
tom 1002
jerry 1003

[root@ubuntu2204 ~]# awk -F: '{if($3<=100){print "<=100",$3}else if ($3<=1000){print "<=1000",$3} else{print ">1000",$3}}' /etc/passwd
<=100 0
<=100 1
<=100 2
<=100 3

[root@ubuntu2204 ~]# awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd
root
mage
jose
tom
jerry

[root@ubuntu2204 ~]# awk '{if(NF>5) print $0}' /etc/fstab
......

[root@ubuntu2204 ~]#awk -F: '{if($3>=1000) {printf "Common user: %s\n",$1} else {printf "root or Sysuser: %s\n",$1}}' /etc/passwd
root or Sysuser: root
root or Sysuser: bin
root or Sysuser: daemon
  • 范例:
[root@ubuntu2204 ~]# df -h|awk -F% '/^\/dev\/sd/{print $1}'| awk '$NF>=10{print $1,$5}'
/dev/sda2 14

[root@ubuntu2204 ~]# df | awk -F"[[:space:]]+|%" '/^\/dev\/sd/{if($5>10)print $1,$5}'
/dev/sda2 14

[root@ubuntu2204 ~]# df | awk -F' +|%' '/^\/dev\/sd/{if($5>=10)print $1,$5}'
/dev/sda2 14

[root@ubuntu2204 ~]# df | awk -F"[ %]+" '/^\/dev\/sd/{if($(NF-1)>10){print $(NF-1)" full"}else {print $(NF-1)" OK"}}'
14 full
  • 范例:
[root@ubuntu2204 ~]# awk 'BEGIN{ test=100;if(test>90){print "very good"}else if(test>60){ print "good"}else{print "no pass"}}'
very good
7.4.3.8 条件判断 switch-case
  • awk 中的switch分支语句功能较弱,只能进行等值比较或正则匹配。
  • 各分支结尾需使用break来终止
  • 语法:
switch(expression) {
	case VALUE1 or /REGEXP1/:
		statement1;
		break;
	case VALUE2 or /REGEXP2/:
		statement2;
		break;
		
	......;
	default:
		statementn;
		break;
}
  • 分支穿透:
switch(expression) {
	case VALUE1 or /REGEXP1/:
	case VALUE2 or /REGEXP2/:
	case VALUE3 or /REGEXP3/:
		statement1;
		break;
	case VALUE4 or /REGEXP4/:
		statement2;
		break;
		
	......;
	default:
		statementn;
		break;
}
  • 范例:
[root@ubuntu2204 ~]# awk -F: '{switch($3){case 0:print "root",$3;break; case
/^[1-9][0-9]{0,2}$|1000/:print "sys user",$3;break;default:print "common
user",$3;break;}}' /etc/passwd
......
7.4.3.9 循环 while
  • 条件“真”,进入循环;条件“假”,退出循环
  • 使用场景: 对一行内的多个字段逐一类似处理时使用;对数组中的各元素逐一处理时使用
  • 语法:
while (condition) {statement;}
  • 范例:
[root@ubuntu2204 ~]# awk -v i=1 -v sum=0 'BEGIN{while(i<=100){sum+=i;i++};print sum}'
5050

[root@ubuntu2204 ~]# awk 'BEGIN{ total=0;i=1;while(i<=100){total+=i;i++};print total}'
5050
7.4.3.10 循环 do-while
  • 无论真假,至少执行一次循环体
  • 语法:
do {statement;}while(condition)
  • 范例:
[root@ubuntu2204 ~]# awk 'BEGIN{ total=0;i=1;do{total+=i;i++;}while(i<=100);print total}'
5050

#至少执行一次循环
[root@ubuntu2204 ~]# awk 'BEGIN{ total=0;i=101;do{ print total }while(i<=100);}'
0
7.4.3.11 循环 for
  • 语法:
for(expr1;expr2;expr3) {statement;}
  • 特殊用法:能够遍历数组中的元素
for(var in array) {for-body}
  • 范例:
[root@ubuntu2204 ~]# awk 'BEGIN{sum=0;for(i=1;i<=100;i++){sum+=i};print sum}'
5050

#shell实现
[root@ubuntu2204 ~]# for((i=1,sum=0;i<=100;i++));do let sum+=i;done;echo $sum
5050
  • 范例:遍历数组
[root@ubuntu2204 ~]# awk 'BEGIN{ arr[0]=123;arr[1]=456;for(i in arr){ print i"==>"arr[i] };}'
0==>123
1==>456
  • 面试题:文件abc.txt只有一行数字,计算其总和
[root@ubuntu2204 ~]# cat abc.txt
1 2 3 4 5
[root@ubuntu2204 ~]# cat abc.txt |awk '{for(i=1;i<=NF;i++){sum+=$i};print sum}'
15
[root@ubuntu2204 ~]# cat abc.txt|tr ' ' + |bc
15
[root@ubuntu2204 ~]# sum=0;for i in `cat abc.txt`;do let sum+=i;done;echo $sum
15
  • 性能比较
time (awk 'BEGIN{ total=0;for(i=0;i<=10000;i++){total+=i;};print total;}')
time (total=0;for i in {1..10000};do total=$(($total+i));done;echo $total)
time (for ((i=0;i<=10000;i++));do let total+=i;done;echo $total)
time (seq -s"+" 10000|bc)
  • 范例: 取出字符串中的数字
[root@ubuntu2204 ~]# echo 'dsFUs34tg*fs5a%8ar%$#@' |awk -F "" '
> {
> 	for(i=1;i<=NF;i++)
> 	{
> 		if ($i ~ /[0-9]/)
> 		{
> 			str=(str $i) 			#连字符,字符串拼接
> 		}
> 	}
> 	print str
> }'
3458
7.4.3.12 continue 和 break
  • continue 中断本次循环
  • break 中断整个循环
  • 格式:
continue [n]
break [n]
  • 范例:
[root@ubuntu2204 ~]# awk 'BEGIN{for(i=1;i<=100;i++){if(i==50)continue;sum+=i};print sum}'
5000

[root@ubuntu2204 ~]# awk 'BEGIN{for(i=1;i<=100;i++){if(i==50)break;sum+=i};print sum}'
1225

[root@ubuntu2204 ~]# awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i%2==0)continue;sum+=i}print sum}'
2500

[root@ubuntu2204 ~]# awk 'BEGIN{sum=0;for(i=1;i<=100;i++){if(i==50)break;sum+=i}print sum}'
1225
7.4.3.13 next
  • next 可以提前结束对本行处理而直接进入下一行处理(awk自身循环)
  • 范例:
[root@ubuntu2204 ~]# awk -F: '{if($3%2!=0){next;}; print $1,$3}' /etc/passwd
root 0
daemon 2
lp 4
shutdown 6
mail 8

[root@ubuntu2204 ~]# seq 10 |awk '{if($0%2!=0){next};{print $0}}'
2
4
6
8
10
7.4.3.14 数组
  • awk的数组为关联数组
  • 格式
array_name[index-expression]
  • index-expression
    • 利用数组,实现 k/v 功能
    • 可使用任意字符串;字符串要使用双引号括起来
    • 如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”
    • 若要判断数组中是否存在某元素,要使用“index in array”格式进行遍历
  • 范例:
[root@ubuntu2204 ~]# awk
'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";print weekdays["mon"]}'
Monday
  • 范例:判断数组索引是否存在
[root@ubuntu2204 ~]# awk 'BEGIN{array["i"]="x"; array["j"]="y" ; print "i" in array,"y" in array }'
1 0

[root@ubuntu2204 ~]# awk 'BEGIN{array["i"]="x"; array["j"]="y" ;if ("i" in array) {print "存在"}else{print "不存在"}}'
存在

[root@ubuntu2204 ~]# awk 'BEGIN{array["i"]="x"; array["j"]="y" ;if ("abc" in array ) {print "存在"}else{print "不存在"}}'
不存在
  • 若要遍历数组中的每个元素,要使用 for 循环
for(var in array) {for-body}
  • 范例:遍历数组
[root@ubuntu2204 ~]# awk
'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays){print i,weekdays[i]}}'
tue Tuesday
mon Monday

[root@ubuntu2204 ~]# awk
'BEGIN{students[1]="user1";students[2]="user2";students[3]="user3";for(x in students){print x":"students[x]}}'
1:user1
2:user2
3:user3

[root@ubuntu2204 ~]# awk 'BEGIN {
a["x"] = "welcome"
a["y"] = "to"
a["z"] = "Magedu"
for (i in a) {
	print i,a[i]
}
}'
x welcome
y to
z Magedu

[root@ubuntu2204 ~]#awk -F: '{user[$1]=$3}END{for(i in user){print "username: "i,"uid: "user[i]}}' /etc/passwd
username: adm uid: 3
username: rpc uid: 32
username: dnsmasq uid: 985
  • 范例:
[root@ubuntu2204 ~]# awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' /var/log/httpd/access_log
172.20.0.200 1482
172.20.21.121 2
172.20.30.91 29
172.16.102.29 864
172.20.0.76 1565
172.20.9.9 15
172.20.1.125 463
172.20.61.11 2
172.20.73.73 198

[root@ubuntu2204 ~]# awk '{ip[$1]++}END{for(i in ip){print ip[i],i}}' access_log |sort -nr| head -3
4870 172.20.116.228
3429 172.20.116.208
2834 172.20.0.222

[root@ubuntu2204 ~]# awk '{ip[$1]++}END{for(i in ip){print i,ip[i]}}' access_log |sort -k2 -nr|head -3
172.20.116.228 4870
172.20.116.208 3429
172.20.0.222 2834
  • 范例:封掉查看访问日志中连接次数超过1000次的IP
[root@ubuntu2204 ~]# awk '{ip[$1]++}END{for(i in ip){if(ip[i]>=1000){system("iptables -A INPUT -s "i" -j REJECT")}}}' nginx.access.log-20200428
  • 范例:多维数组
[root@ubuntu2204 ~]# awk 'BEGIN{
> array[1][1]=11
> array[1][2]=12
> array[1][3]=13
> array[2][1]=21
> array[2][2]=22
> array[2][3]=23
> for (i in array)
> 	for (j in array[i])
> 		print array[i][j]
> }'
11
12
13
21
22
23
  • 范例:
[root@ubuntu2204 ~]# cat score.txt
name sex score
alice f 100
bob m 90
ming m 95
hong f 90

[root@ubuntu2204 ~]# awk 'NR!=1{if($2=="m")
{m_sum+=$3;m_num++}else{f_sum+=$3;f_num++}}END{print "男生平均成绩="m_sum/m_num,"女
生平均成绩="f_sum/f_num}' score.txt
男生平均成绩=92.5 女生平均成绩=95

[root@ubuntu2204 ~]# awk 'NR!=1{score[$2]+=$3;num[$2]++}END{for(i in score)
{print i,score[i]/num[i]}}' score.txt
m 92.5
f 95

[root@ubuntu2204 ~]# awk 'NR!=1{score[$2]+=$3;num[$2]++}END{for(i in score)
{if(i=="m"){print "男生平均成绩=",score[i]/num[i]}else{print "女生平均成绩
=",score[i]/num[i]}}}' score.txt
男生平均成绩= 92.5
女生平均成绩= 95
7.4.3.15 awk 函数
7.4.3.15.1 常见内置函数
  • 数值处理
rand() 		#返回01之间一个随机数
srand() 	#配合rand() 函数,生成随机数的种子
int() 		#返回整数
  • 范例:
[root@ubuntu2204 ~]# awk 'BEGIN{srand();print rand()}'
0.790437

[root@ubuntu2204 ~]# awk 'BEGIN{srand();print rand()}'
0.283736

[root@ubuntu2204 ~]# awk 'BEGIN{srand(); for (i=1;i<=5;i++)print int(rand()*100)}'
35
17
35
95
19
  • 字符串处理
字符串处理含义
str1=(str2,str3)连字符,字符串拼接
length([s])返回指定字符串的长度
sub(r,s,[t])在t中串搜索r匹配的内容,并将第一个匹配内容替换为s
gsub(r,s,[t])在t中串搜索r匹配的内容,并将匹配内容全部替换为s,即贪婪模式
split(s,array,[r])以r为分隔符,切割串s,并将结果保存至array数组中,索引值为1,2,…
  • 范例: 统计用户名的长度
[root@ubuntu2204 ~]# cut -d: -f1 /etc/passwd | awk '{print length()}'
[root@ubuntu2204 ~]# awk -F: '{print length($1)}' /etc/passwd
  • 范例:
[root@ubuntu2204 ~]# echo "2008:08:08 08:08:08" | awk 'sub(/:/,"-",$1)'
2008-08:08 08:08:08

[root@ubuntu2204 ~]# echo "2008:08:08 08:08:08" | awk '{sub(/:/,"-",$1);print $0}'
2008-08:08 08:08:08
  • 范例:
[root@ubuntu2204 ~]# echo "2008:08:08 08:08:08" | awk 'gsub(/:/,"-",$0)'
2008-08-08 08-08-08

[root@ubuntu2204 ~]# echo "2008:08:08 08:08:08" | awk '{gsub(/:/,"-",$0);print $0}'
2008-08-08 08-08-08
  • 范例:
[root@ubuntu2204 ~]# netstat -tn | awk
'/^tcp/{split($4,ip,":");count[ip[1]]++}END{for(i in count){print i,count[i]}}'
10.0.0.1 1
10.0.0.6 1
10.0.0.7 673
  • 调用shell命令
system('cmd')
  • 空格是awk中的字符串连接符,如果system中需要使用awk中的变量可以使用空格分隔,或者说除了awk的变量外其他一律用""引用起来
[root@ubuntu2204 ~]# awk 'BEGIN{system("hostname")}'
rocky86.m51.magedu.com

[root@ubuntu2204 ~]# awk 'BEGIN{score=100; system("echo your score is " score)}'
your score is 100

[root@ubuntu2204 ~]# netstat -tn | awk
'/^tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count){if(count[i]>=10)
{system("iptables -A INPUT -s "i" -j REJECT")}}}'
  • 时间函数
systime() #当前时间到197011日的秒数
strftime() #指定时间格式
  • 范例:
[root@ubuntu2204 ~]# awk 'BEGIN{print systime()}'
1609917829

[root@ubuntu2204 ~]# awk 'BEGIN{print systime();system("date +%s")}'
1661687319
1661687319

[root@ubuntu2204 ~]# awk 'BEGIN{print strftime("%Y-%m-%dT%H:%M",systime()-3600)}'
2021-01-06T14:24

[root@ubuntu2204 ~]# awk 'BEGIN{print strftime("%Y-%m-%d %H:%M:%S");system("date \"+%Y-%m-%d %H:%M:%S\"")}'
2022-08-28 19:51:34
2022-08-28 19:51:34
7.4.3.15.2 自定义函数
  • 自定义函数格式:
function name ( parameter, parameter, ... ) {
	statements
	return expression
}
  • 范例:
[root@ubuntu2204 ~]# awk 'function test(){print "hello test func"}BEGIN{test()}'
hello test func

[root@ubuntu2204 ~]# awk -F: 'function test(uname,uid){print uname" id is "uid}{test($1,$3)}' /etc/passwd
root id is 0
bin id is 1
daemon id is 2
[root@ubuntu2204 ~]# cat func.awk
function max(x,y) {
x>y?var=x:var=y
return var
}
BEGIN{print max(a,b)}

[root@ubuntu2204 ~]# awk -v a=30 -v b=20 -f func.awk
30
7.4.3.16 awk 脚本
  • 将awk程序写成脚本,直接调用或执行
  • 范例:
[root@ubuntu2204 ~]# cat passwd.awk
{if($3>=1000)print $1,$3}

[root@ubuntu2204 ~]# awk -F: -f passwd.awk /etc/passwd
nobody 65534
wang 1000
mage 1001
[root@ubuntu2204 ~]# cat test.awk
#!/bin/awk -f
{if($3>=1000)print $1,$3}
[root@ubuntu2204 ~]# chmod +x test.awk
[root@ubuntu2204 ~]# ./test.awk -F: /etc/passwd
nobody 65534
wang 1000
mage 1001
  • 向awk脚本传递参数
  • 格式:
awkfile var=value var2=value2... Inputfile
  • 注意:
    • 上面格式变量在BEGIN过程中不可用。直到首行输入完成以后,变量才可用
    • -可以通过-v 参数,让awk在执行BEGIN之前得到变量的值
    • 命令行中每一个指定的变量都需要一个-v参数
  • 范例:
[root@ubuntu2204 ~]# awk -v x=100 'BEGIN{print x}{print x+100}' /etc/hosts
100
200
200

[root@ubuntu2204 ~]# awk 'BEGIN{print x}{print x+100}' x=200 /etc/hosts
300
300
  • 范例:
[root@ubuntu2204 ~]# cat test2.awk
#!/bin/awk -f
{if($3 >=min && $3<=max)print $1,$3}

[root@ubuntu2204 ~]# chmod +x test2.awk

[root@ubuntu2204 ~]# ./test2.awk -F: min=100 max=200 /etc/passwd
systemd-resolve 193
rtkit 172
pulse 171
qemu 107
usbmuxd 113
abrt 173

7.5. 练习

7.5.1、文件host_list.log 如下格式,请提取”.magedu.com”前面的主机名部分并写入到回到该文件中

1 www.magedu.com
2 blog.magedu.com
3 study.magedu.com
4 linux.magedu.com
5 python.magedu.com
......
999 study.magedu.com

参考答案:

[root@ubuntu2204 ~]# awk -F'[ .]' '{print $2}' host_list.log >> host_list.log
[root@ubuntu2204 ~]# cat host_list.log
1 www.magedu.com
1 www.magedu.com
2 blog.magedu.com
3 study.magedu.com
4 linux.magedu.com
5 python.magedu.com
......
999 study.magedu.com
www
blog
study
linux
python

7.5.2、统计/etc/fstab文件中每个文件系统类型出现的次数

参考答案

[root@rocky86 ~]# awk -F' +' '/^UUID/{fs[$3]++}END{for(i in fs){print i,fs[i]}}'
/etc/fstab
swap 1
ext4 3

[root@rocky86 ~]# awk -F' +' '/^UUID/{print $3}' /etc/fstab |uniq -c
	3 ext4
	1 swap

7.5.3、统计/etc/fstab文件中每个单词出现的次数

参考答案

[root@rocky86 ~]# awk -F"[^[:alpha:]]" '{for(i=1;i<=NF;i++)word[$i]++}END{for (a in word)if(a !="") print a,word[a]}' /etc/fstab

7.5.4、提取出字符串Yd$C@M05MB%9&Bdh7dq+YVixp3vpw中的所有数字

参考答案

[root@rocky86 ~]# echo 'Yd$C@M05MB%9&Bdh7dq+YVixp3vpw' | awk '{gsub(/[^0-9]/,"");print $0}'
05973

[root@rocky86 ~]# echo 'Yd$C@M05MB%9&Bdh7dq+YVixp3vpw' |awk -F "" '{for(i=1;i<=NF;i++){if ($i ~ /[[:digit:]]/){str=$i; str1=(str1 str)}};print str1}'
05973

[root@rocky86 ~]# echo 'Yd$C@M05MB%9&Bdh7dq+YVixp3vpw' | awk -F'[^0-9]' '{for(i=1;i<=NF;i++){printf "%s",$i }}'
05973

7.5.5、文件random.txt记录共5000个随机的整数,存储的格式100,50,35,89…请取出其中最大和最小的整数

参考答案

[root@rocky86 ~]# str="";for((i=1;i<=5000;i++));do if [ $i -ne 5000 ];then str+="$RANDOM,";else str+=$RANDOM;fi;done;echo $str > random.txt

[root@rocky86 ~]# awk -F, '{max=$1;min=$1;for(i=1;i<=NF;i++){if($i>max) {max=$i}else{if($i<min){min=$i}}}}END{print "最大值:"max,"最小值:"min}' random.txt

在这里插入图片描述

7.5.6、解决Dos攻击生产案例:监控当某个IP并发连接数超过100时,即调用防火墙命令封掉对应的IP,监控频率每隔5分钟。防火墙命令为:iptables -A INPUT -s IP -j REJECT

参考答案

[root@rocky86 ~]# ss -nt | awk -F " +|:" 'NR!=1{ip[$(NF-2)]++}END{for(i in ip) {if(ip[i]>100){system("iptables -A INPUT -s "i" -j REJECT")}}}'

7.5.7、将以下文件内容中FQDN取出并根据其进行计数从高到低排序

http://mail.magedu.com/index.html
http://www.magedu.com/test.html
http://study.magedu.com/index.html
http://blog.magedu.com/index.html
http://www.magedu.com/images/logo.jpg
http://blog.magedu.com/20080102.html
http://www.magedu.com/images/magedu.jpg

参考答案:

[root@centos8 ~]#awk -F"/" '{url[$3]++}END{for(i in url){print url[i],i}}' url.log |sort -nr
3 www.magedu.com
2 blog.magedu.com
1 study.magedu.com
1 mail.magedu.com

7.5.8、将以下文本文件awktest.txt中 以inode列为标记,对inode列相同的counts列进行累加,并且统计出同一inode中,beginnumber列中的最小值和endnumber列中的最大值

inode|beginnumber|endnumber|counts|
106|3363120000|3363129999|10000|
106|3368560000|3368579999|20000|
310|3337000000|3337000100|101|
310|3342950000|3342959999|10000|
310|3362120960|3362120961|2|
311|3313460102|3313469999|9898|
311|3313470000|3313499999|30000|
311|3362120962|3362120963|2|

输出的结果格式为:

106|3363120000|3368579999|30000|
310|3337000000|3362120961|10103|
311|3313460102|3362120963|39900|

参考答案:

[root@centos8 ~]#cat awktest.txt
inode|beginnumber|endnumber|counts|
106|3363120000|3363129999|10000|
106|3368560000|3368579999|20000|
310|3337000000|3337000100|101|
310|3342950000|3342959999|10000|
310|3362120960|3362120961|2|
311|3313460102|3313469999|9898|
311|3313470000|3313499999|30000|
311|3362120962|3362120963|2|

[root@rocky86 ~]# awk -F '|'
'!/^inode/{sum[$1]+=$4;if(!begin[$1])begin[$1]=$2;else
if(begin[$1]>$2)begin[$1]=$2;if(!end[$1])end[$1]=$3;else if(end[$1]
<$3)end[$1]=$3}END{for(i in sum)print i"|"begin[i]"|"end[i]"|"sum[i]}'
awktest.txt

[root@rocky86 ~]# awk -F'|' -v OFS='|' '/^[0-9]/{inode[$1]++; if(!bn[$1])
{bn[$1]=$2}else if(bn[$1]>$2){bn[$1]=$2}; if(en[$1]<$3)en[$1]=$3;cnt[$1]+=$(NF-
1)} END{for(i in inode)print i,bn[i],en[i],cnt[i]}' awktest.txt

endl

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

良辰美景好时光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值