通配符

通配符是shell在做PathnameExpansion时用到的。说白了一般只用于文件名匹配,它是由shell解析的,比如 find,ls,cp,mv等 shell常见通配符

1
2
3
4
5
6
*:匹配0或多个字符
?:匹配任意一个字符
[list]:匹配list中任意单个字符
[c1‐c2]:匹配c1‐c2中任意单个字符
[^c1‐c2]/[!c1‐c2]:不匹配c1‐c2中任意字符
{string1,string2,...}:匹配{}中任意单个字符串
  • shell 元字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
IFS:<tab>/<space>/<enter>
CR:<enter>
=:设定变量
$:取变量值
>/< :重定向
|:管道
&:后台执行命令
():在子shell中执行命令/运算或命令替换
{}:函数中执行/变量替换的界定范围
;:命令结束后,忽略其返回值,继续执行下一个命令
&&:命令结束后,若为true,继续执行下一个命令
||:命令结束后,若为false,继续执行下一个命令
!:非
#:注释
\:转义符
  • shell转义符
1
2
'':硬转义,内部所有shell元字符,通配符都会被关掉
"":软转义,内部

find文件查找

  • 实时查找工具,通过遍历指定路径下的文件系统完成文件查找
  • 工作特点
    • 查找速度略慢
    • 精确查找
    • 实时查找
    • 可以满足多种条件匹配
1
2
3
4
find [选项] [路径] [查找条件 + 处理动作]
查找路径:指定具体目录路径,默认是当前文件夹
查找条件:指定的查找标准(文件名/大小/类型/权限等),默认是找出所有文件
处理动作:对符合条件的文件做什么操作,默认输出屏幕

查找条件

  • 查找条件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
根据文件名查找:
‐name "filename" 支持global
‐iname "filename" 忽略大小写
‐regex "PATTERN" 以Pattern匹配整个文件路径字符串,而不仅仅是文件名称
根据属主和属组查找:
‐user USERNAME:查找属主为指定用户的文件
‐group GROUPNAME:查找属组为指定属组的文件
‐uid UserID:查找属主为指定的ID号的文件
‐gid GroupID:查找属组为指定的GID号的文件
‐nouser:查找没有属主的文件
‐nogroup:查找没有属组的文件
根据文件类型查找:
‐type Type:
f/d/l/s/b/c/p
根据文件大小来查找:
‐size [+|‐]N[bcwkMG]
根据时间戳:
天:
‐atime [+|‐]N
‐mtime
‐ctime
分钟:
‐amin N
‐cmin N
‐mmin N
根据权限查找:
‐perm [+|‐]MODE
MODE:精确权限匹配
/MODE:任何一类(u,g,o)对象的权限中只要能一位匹配即可
‐MODE:每一类对象都必须同时拥有为其指定的权限标准
组合条件:
与:‐a
或:‐o
非:‐not
相关案例:找出/tmp目录下,属主不是root,且文件名不是fstab的文件:
find /tmp \( -not -user root -a -not -name fstab \) -ls
  • 根据文件名查找
1
2
3
[root@localhost ~]# find /etc -name "ifcfg-ens33"
[root@localhost ~]# find /etc -iname "ifcfg-ens33" # 忽略大小写
[root@localhost ~]# find /etc -iname "ifcfg*"
  • 按文件大小
1
2
3
4
[root@localhost ~]# find /etc -size +5M    # 大于5M
[root@localhost ~]# find /etc -size 5M # 等于5M
[root@localhost ~]# find /etc -size -5M # 小于5M
[root@localhost ~]# find /etc -size +5M -ls # 找到的处理动作-ls
  • 指定查找的目录深度
1
2
3
[root@localhost ~]# find / -maxdepth 3 -a -name "ifcfg-ens33"    # 最大查找深度
# -a是同时满足,-o是或
[root@localhost ~]# find / -mindepth 3 -a -name "ifcfg-ens33" # 最小查找深度
  • 按时间找
1
2
3
[root@localhost ~]# find /etc -mtime +5        # 修改时间超过5天
[root@localhost ~]# find /etc -mtime 5 # 修改时间等于5天
[root@localhost ~]# find /etc -mtime -5 # 修改时间5天以内
  • 按照文件属主、属组找
1
2
3
4
5
6
7
[root@localhost ~]# find /home -user xwz    # 属主是xwz的文件
[root@localhost ~]# find /home -group xwz
[root@localhost ~]# find /home -user xwz -group xwz
[root@localhost ~]# find /home -user xwz -a -group root
[root@localhost ~]# find /home -user xwz -o -group root
[root@localhost ~]# find /home -nouser # 没有属主的文件
[root@localhost ~]# find /home -nogroup # 没有属组的文件
  • 按文件类型
1
[root@localhost ~]# find /dev -type d
  • 按文件权限
1
2
3
4
[root@localhost ~]# find / -perm 644 -ls      
[root@localhost ~]# find / -perm -644 -ls # 权限小于644的
[root@localhost ~]# find / -perm 4000 -ls
[root@localhost ~]# find / -perm -4000 -ls
  • 按正则表达式
1
2
3
[root@localhost ~]# find /etc -regex '.*ifcfg-ens[0-9][0-9]'
# .* 任意多个字符
# [0-9] 任意一个数字
  • 条件组合
1
2
3
-a:多个条件and并列
-o:多个条件or并列
-not:条件取反

处理动作

1
2
3
4
5
6
7
8
9
10
11
print:默认的处理动作,显示至屏幕
ls:类型于对查找到的文件执行“ls ‐l”命令
‐delete:删除查找到的文件
‐fls /path/to/somefile:查找到的所有文件的长格式信息保存至指定文件中
‐ok COMMAND {}\:对查找到的每个文件执行由COMMAND指定的命令
对于每个文件执行命令之前,都会交换式要求用户确认
exec COMMAND {} \:对查找到的每个文件执行由COMMAND指定的命令
[root@server1 ~]# find /etc/init.d/ -perm -111 -exec cp -r {} dir1/ \;
{}:用于引用查找到的文件名称自身
注意:find传递查找到的文件至后面指定的命令时,查找到所有符合条件的文件一次性传递给后面的命令;另一种方式可规避此问题
find | xargs COMMAND

正则表达式

正则表达式是用来匹配字符串的,针对文件内容的文本过滤工具里,大都用到正则表达式,如vi,grep,awk, sed等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
##字符匹配
.:匹配任意单个字符
[]:匹配指定范围内任意单个字符 [a-z] [0-9]
[^]:匹配指定范围外任意单个字符 [^a-z] [^0-9]
[:alnum:]:字母与数字字符
[:alpha:]:字母
[:ascii:]:ASCII字符
[:blank:]:空格或制表符
[:cntrl:]:ASCII控制字符
[:digit:]:数字
[:graph:]:非控制、非空格字符
[:lower:]:小写字母
[:print:]:可打印字符
[:punct:]:标点符号字符
[:space:]:空白字符,包括垂直制表符
[:upper:]:大写字母
[:xdigit:]:十六进制数字
##匹配次数
*:匹配前面的字符任意次数
.*:匹配任意长度的字符
\?:匹配其前面字符0或1次,即前面的可有可无 'a\?b'
\+:匹配其前面的字符至少1次 'a\+b'
\{m\}:匹配前面的字符m次
\{m,n\}:匹配前面的字符至少m次,至多n次
\{0,n\}:匹配前面的字符至多n次
\{m,\}:匹配前面的字符至少m次
##位置锚定
^:行首锚定,用于模式的最左侧
$:行末锚定,用于模式的最右侧
^PATTERN$:用于模式匹配整行;
^$:空行
\< 或 \b:词首锚定,用于单词模式的左侧
\> 或 \b:词尾锚定,用于单词模式的右侧
\<PATTERN\>:匹配整个单词 '\<hello\>'
##分组
\(\):将一个或多个字符捆绑在一起;当作一个字符
\(xy\)*ab
Note:分组括号中的模式匹配到的内容会被正则表达式引擎记录于内部的变量中,这些变量的命令方式为:
\1,\2,\3……
\1:从左侧起,第一个左括号以及与之匹配右括号之间的模式所匹配到的字符;
\(ab\+\(xy\)*\):
\1:ab\+\(xy\)*
\2:xy
  • 扩展正则表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
##字符匹配
.
[]
[^]
##次数匹配
*
?:0次或1次
+:1次或多次
{m}:匹配m次
{m,n}:至少m次,至多n次
##位置锚定
##分组
()
##或者
a|b
C | cat :C或cat
(C|c)at : C或c

Linux三剑客之grep

grep作用:过滤文本内容

选项 描述
-E :–extended–regexp 模式是扩展正则表达式(ERE)
-i :–ignore–case 忽略大小写
-n: –line–number 打印行号
-o:–only–matching 只打印匹配的内容
-c:–count 只打印每个文件匹配的行数
-B:–before–context=NUM 打印匹配的前几行
-A:–after–context=NUM 打印匹配的后几行
-C:–context=NUM 打印匹配的前后几行
–color[=WHEN] 匹配的字体颜色,别名已定义了
-v:–invert–match 打印不匹配的行
-e 多点操作eg:grep -e “^s” -e “s$”

样本文件内容

1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost ~]# cat m 
asdkahsduoa
aslkdsl
oiofr
sdjo
A
F
aSDD
CASDC

asdo
ca

实例

实例1:打印出所有的a无论大小写 : -i选项

1
2
3
4
5
6
7
8
[root@localhost ~]# grep -i "a" test
asdkahsduoa
aslkdsl
A
aSDD
CASDC
asdo
ca

实例2:打印出所有的a无论大小写,并且显示该字符串所在的行 : -n选项

1
2
3
4
5
6
7
8
[root@localhost ~]# grep -in "a" test
1:asdkahsduoa
2:aslkdsl
5:A
7:aSDD
8:CASDC
10:asdo
11:ca

实例3:仅仅打印出所有匹配的字符串: -o选项

1
2
3
4
5
6
7
8
9
10
[root@localhost ~]# grep -io "a" test
a
a
a
a
A
a
A
a
a

实例4:打印出匹配的字符串有多少行 -c选项

1
2
[root@localhost ~]# grep -ic "a" test
7

实例5:打印出字符S前面的2行 -B

1
2
3
4
5
[root@localhost ~]# grep -B 2 "S" test
A
F
aSDD
CASDC

实例6:打印出字符S后面的2行 -A

1
2
3
4
5
[root@localhost ~]# grep -A 2 "S" test
aSDD
CASDC

asdo

实例7:打印出字符S前后2行 -C

1
2
3
4
5
6
7
[root@localhost ~]# grep -C 2 "S" test
A
F
aSDD
CASDC

asdo

实例8:打印出不包含大小s的所有行 取反 -v

1
2
3
4
5
6
[root@localhost ~]# grep -iv "S" test
oiofr
A
F

ca

grep可以从文件当中直接搜索某个关键词,也可以从标准输入里面搜索

1
2
3
4
5
6
[root@localhost ~]# grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@localhost ~]# cat /etc/passwd | grep "root"
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

正则表达式(基于grep)

  • 功能就是用来检索、替换那些符合某个模式(规则)的文本,正则表达式在每种语言中都会有;
  • 正则表达式就是为了处理大量的文本或字符串而定义的一套规则和方法
  • 通过定义的这些特殊符号的辅助,系统管理员就可以快速过滤,替换或输出需要的字符串
  • Linux正则表达式一般以行为单位处理

基础正则表达式

符号 描述
. 匹配任意单个字符(必须存在)
^ 匹配以某个字符开头的行
$ 配以什么字符结尾的行
* 匹配前面的一个字符出现0次或者多次;eg:a*b
.* 表示任意长度的任意字符
[] 表示匹配括号内的一个字符
[^] 匹配[^字符]之外的任意一个字符
[] 匹配非[^字符]内字符开头的行
< 锚定 单词首部;eg:<root
> 锚定 单词尾部:eg:root>
{m,n} 表示匹配前面的字符出现至少m次,至多n次
() 表示对某个单词进行分组;\1表示第一个分组进行调用

扩展正则

  • egrep …
  • grep -E …
  • 扩展正则支持所有基础正则;并有补充
  • 扩展正则中{}和[]不用转义可以直接使用;
符号 描述
+ 表示前面的字符至少出现1次的情况
\ 表示“或”
表示前面的字符至多出现1次的情况

最常用

查看配置文件时去除所有的注释和空行

1
[root@localhost ~]# grep -Ev "^#|^$" /etc/ssh/sshd_config

这里解释下:

grep -Ev "^#|^$" /etc/ssh/sshd_config 是一个Linux命令,用于从/etc/ssh/sshd_config文件中筛选出非注释行和非空行。这里使用了grep命令,结合正则表达式和选项来实现这一目标。

  • grep: 是一个强大的文本搜索工具,用于搜索文件中的特定模式。

  • -E: 告诉grep使用扩展正则表达式(Extended Regular Expression)。这允许使用更高级的正则表达式语法。

  • -v: 表示反向选择,即选择不匹配指定模式的行。

  • "^#|^$"
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57

    : 是一个扩展正则表达式,它匹配两种类型的行:

    - `^#`: 以`#`字符开头的行,这通常是注释行。
    - `^$`: 空行,即只包含行首和行尾的行。

    - `/etc/ssh/sshd_config`: 是要搜索的文件路径,该文件通常包含SSH服务器的配置选项。

    因此,`grep -Ev "^#|^$" /etc/ssh/sshd_config`这个命令的作用是从`/etc/ssh/sshd_config`文件中筛选出所有非注释行和非空行,并将这些行输出到终端。这样,你可以更容易地查看和编辑SSH服务器的实际配置选项,而不必被注释和空行所干扰。

    ~~~
    sed '/^\s*#/d; /^\s*$/d' filename | grep -v '^$'
    ~~~

    `sed '/^\s*#/d; /^\s*$/d' filename | grep -v '^$'` 是一个在Linux shell中使用的命令组合,用于处理文本文件并输出特定的内容。这个命令组合的目的是从文件中去除所有的注释行和空行。下面我逐一解释每个部分的作用:

    `sed` 是一个流编辑器,用于对输入流(或文件)进行基本的文本转换。在这个命令中,`sed` 接收两个正则表达式作为编辑命令,并应用于文件 `filename`。

    1. `/^\s*#/d`:这个命令告诉 `sed` 删除所有以零个或多个空白字符(`\s*`)开头,后面紧跟一个 `#` 符号的行。这通常用于删除以 `#` 开头的注释行。
    2. `/^\s*$/d`:这个命令告诉 `sed` 删除所有只包含零个或多个空白字符的行。这用于删除空行。

    `grep` 是一个强大的文本搜索工具,用于搜索特定的模式。在这里,`-v` 选项表示“反向选择”,即选择不匹配指定模式的行。

    `'^$'` 是一个正则表达式,匹配只包含行首和行尾的行,即空行。因此,`grep -v '^$'` 会从输入中排除所有空行。

    将 `sed` 和 `grep` 命令通过管道 `|` 连接起来,可以首先使用 `sed` 删除注释和空行,然后使用 `grep` 进一步删除可能由 `sed` 留下的任何额外的空行。这通常用于确保输出中没有空行。

    综合来说,`sed '/^\s*#/d; /^\s*$/d' filename | grep -v '^$'` 这个命令组合会从 `filename` 文件中删除所有的注释行和空行,并将结果输出到终端。

    # Linux三剑客之sed

    Linux sed命令是利用script来处理文本文件。

    sed可依照script的指令,来处理、编辑文本文件。

    sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。

    ## 语法

    ```bash
    sed的命令格式: sed [option] 'sed command' filename
    sed的脚本格式:sed [option] ‐f 'sed script' filename
    常用选项:
    ‐n :只打印模式匹配的行
    ‐e :直接在命令行模式上进行sed动作编辑,此为默认选项
    ‐f :将sed的动作写在一个文件内,用–f filename 执行filename内的sed动作
    ‐r :支持扩展表达式
    ‐i :直接修改文件内容
    查询文本的方式
    使用行号和行号范围
    x:行号
    x,y:从x行到y行
    x,y!:x行到y行之外
    /pattern:查询包含模式的行
    /pattern/, /pattern/:查询包含两个模式的行
    /pattern/,x:x行内查询包含模式的行
    x,/pattern/:x行后查询匹配模式的行

动作说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
常用选项:
p:打印匹配的行(‐n)
=:显示文件行号
a\:指定行号后添加新文本
i\:指定行号前添加新文本
d:删除定位行
c\:用新文本替换定位文本
w filename:写文本到一个文件
r filename:从另一个文件读文本
s///:替换
替换标记:
g:行内全局替换
p:显示替换成功的行
w:将替换成功的结果保存至指定文件
q:第一个模式匹配后立即退出
{}:在定位行执行的命令组,用逗号分隔
g:将模式2粘贴到/pattern n/

a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)

c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!

d :删除,因为是删除啊,所以 d 后面通常不接任何东西;

i :插入, i的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);

p :打印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行

s :取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法!例如 1,20s/old/new/g 就是啦!

实例

在testfile文件的第四行后添加一行,并将结果输出到标准输出

1
2
3
4
5
6
7
[root@localhost ~]# sed -e 4a\newline test
line one
line two
line three
line four
newline
line five

以行为单位的新增/删除(nl是个整体,不可分割)

将 /etc/passwd 的内容列出并且列印行号,同时,请将第 2~5 行删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@localhost ~]# nl /etc/passwd | sed '2,5d'
1 root:x:0:0:root:/root:/bin/bash
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 operator:x:11:0:operator:/root:/sbin/nologin
11 games:x:12:100:games:/usr/games:/sbin/nologin
12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
13 nobody:x:99:99:Nobody:/:/sbin/nologin
14 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
15 dbus:x:81:81:System message bus:/:/sbin/nologin
16 polkitd:x:999:998:User for polkitd:/:/sbin/nologin
17 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
18 postfix:x:89:89::/var/spool/postfix:/sbin/nologin

sed 的动作为 ‘2,5d’ ,那个 d 就是删除!因为 2-5 行给他删除了,所以显示的数据就没有 2-5 行罗~ 另外,注意一下,原本应该是要下达 sed -e 才对,没有 -e 也行啦!同时也要注意的是, sed 后面接的动作,请务必以 ‘’ 两个单引号括住喔!

只要删除第 2 行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@localhost ~]# nl /etc/passwd | sed '2d'
1 root:x:0:0:root:/root:/bin/bash
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 operator:x:11:0:operator:/root:/sbin/nologin
11 games:x:12:100:games:/usr/games:/sbin/nologin
12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
13 nobody:x:99:99:Nobody:/:/sbin/nologin
14 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
15 dbus:x:81:81:System message bus:/:/sbin/nologin
16 polkitd:x:999:998:User for polkitd:/:/sbin/nologin
17 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
18 postfix:x:89:89::/var/spool/postfix:/sbin/nologin

要删除第 3 到最后一行

1
2
3
[root@localhost ~]# nl /etc/passwd | sed '3,$d'
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin

在第二行后(亦即是加在第三行)加上『hello world』字样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@localhost ~]# nl /etc/passwd | sed '2a\hello world'
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
hello world
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 operator:x:11:0:operator:/root:/sbin/nologin
11 games:x:12:100:games:/usr/games:/sbin/nologin
12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
13 nobody:x:99:99:Nobody:/:/sbin/nologin
14 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
15 dbus:x:81:81:System message bus:/:/sbin/nologin
16 polkitd:x:999:998:User for polkitd:/:/sbin/nologin
17 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
18 postfix:x:89:89::/var/spool/postfix:/sbin/nologin

加在第二行前面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@localhost ~]# nl /etc/passwd | sed '2i\hello world'
1 root:x:0:0:root:/root:/bin/bash
hello world
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 operator:x:11:0:operator:/root:/sbin/nologin
11 games:x:12:100:games:/usr/games:/sbin/nologin
12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
13 nobody:x:99:99:Nobody:/:/sbin/nologin
14 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
15 dbus:x:81:81:System message bus:/:/sbin/nologin
16 polkitd:x:999:998:User for polkitd:/:/sbin/nologin
17 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
18 postfix:x:89:89::/var/spool/postfix:/sbin/nologin

增加多行文字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@localhost ~]# nl /etc/passwd | sed '2a\hello world\
> nihao'
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
hello world
nihao
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 operator:x:11:0:operator:/root:/sbin/nologin
11 games:x:12:100:games:/usr/games:/sbin/nologin
12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
13 nobody:x:99:99:Nobody:/:/sbin/nologin
14 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
15 dbus:x:81:81:System message bus:/:/sbin/nologin
16 polkitd:x:999:998:User for polkitd:/:/sbin/nologin
17 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
18 postfix:x:89:89::/var/spool/postfix:/sbin/nologin

以行为单位的替换与显示

将第2-5行的内容取代成为『No 2-5 number』

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@localhost ~]# nl /etc/passwd | sed '2,5c\No 2-5 number'
1 root:x:0:0:root:/root:/bin/bash
No 2-5 number
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 operator:x:11:0:operator:/root:/sbin/nologin
11 games:x:12:100:games:/usr/games:/sbin/nologin
12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
13 nobody:x:99:99:Nobody:/:/sbin/nologin
14 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
15 dbus:x:81:81:System message bus:/:/sbin/nologin
16 polkitd:x:999:998:User for polkitd:/:/sbin/nologin
17 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
18 postfix:x:89:89::/var/spool/postfix:/sbin/nologin

仅列出 /etc/passwd 文件内的第 5-7 行

1
2
3
4
[root@localhost ~]# nl /etc/passwd | sed -n '5,7p'
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

数据的搜寻并显示

搜索 /etc/passwd有root关键字的行

1
2
3
[root@localhost ~]# nl /etc/passwd | sed -n '/root/p'
1 root:x:0:0:root:/root:/bin/bash
10 operator:x:11:0:operator:/root:/sbin/nologin

数据的搜寻并删除

删除/etc/passwd所有包含root的行,其他行输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@localhost ~]# nl /etc/passwd | sed '/root/d'
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
11 games:x:12:100:games:/usr/games:/sbin/nologin
12 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
13 nobody:x:99:99:Nobody:/:/sbin/nologin
14 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
15 dbus:x:81:81:System message bus:/:/sbin/nologin
16 polkitd:x:999:998:User for polkitd:/:/sbin/nologin
17 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
18 postfix:x:89:89::/var/spool/postfix:/sbin/nologin

数据的搜寻并执行命令

搜索/etc/passwd,找到root对应的行,执行后面花括号中的一组命令,每个命令之间用分号分隔,这里把bash替换为blueshell,再输出这行

1
2
[root@localhost ~]# nl /etc/passwd | sed -n '/root/{s/bash/blueshell/p;q}'
1 root:x:0:0:root:/root:/bin/blueshell

最后的q是退出,不然会继续找下去

数据的搜寻并替换

除了整行的处理模式之外, sed 还可以用行为单位进行部分数据的搜寻并取代

1
sed 's/要被取代的字串/新的字串/g'

查询 IP

原始信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
yum -y install net-tools
[root@localhost ~]# ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.91.128 netmask 255.255.255.0 broadcast 192.168.91.255
inet6 fe80::2de4:b37a:36e9:ae2e prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:d3:76:83 txqueuelen 1000 (Ethernet)
RX packets 33461 bytes 32133707 (30.6 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 11322 bytes 1300148 (1.2 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 10 bytes 697 (697.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 10 bytes 697 (697.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

取有IP地址那一行

1
2
3
[root@localhost ~]# ifconfig | sed -n '/netmask/p'
inet 192.168.91.128 netmask 255.255.255.0 broadcast 192.168.91.255
inet 127.0.0.1 netmask 255.0.0.0

注:-n 选项告诉 sed 不要输出任何内容,除非明确指定要输出。

/netmask/p 是一个 sed 命令,它告诉 sed 查找包含“netmask”的行,并打印(p)这些行。

删除IP地址前面和后面的东西

1
2
3
[root@localhost ~]# ifconfig | sed -n '/netmask/p' | sed 's/^.*inet //g' | sed 's/  netmask.*$//g'
192.168.91.128
127.0.0.1

注:

  1. ifconfig:

ifconfig 命令用于显示或配置网络接口的参数。当你运行 ifconfig,它会列出所有网络接口及其相关的配置信息,如IP地址、子网掩码等。

  1. sed -n '/netmask/p':

这个 sed 命令用于从 ifconfig 的输出中筛选出包含 “netmask” 的行。-n 选项告诉 sed 默认不输出任何内容,而 /netmask/p 则指定只打印包含 “netmask” 的行。

  1. sed 's/^.\*inet //g':

这个 sed 命令用于处理上一步输出的行。s/^.*inet //g 是一个替换命令,它的作用是将每行中从开头到 “inet “ 的部分替换为空(即删除)。这通常用于从 ifconfig 的输出中提取IP地址,因为IP地址通常紧跟在 “inet “ 后面。

  1. sed 's/ netmask.\*$//g':

这个 sed 命令进一步处理上一步的输出。s/ netmask.*$//g 也是一个替换命令,它的作用是将每行中从 “ netmask” 开始到行尾的部分替换为空(即删除)。这用于删除IP地址后面的子网掩码和其他相关信息,只保留IP地址本身。

综合以上步骤,整个命令组合的作用是从 ifconfig 的输出中提取每个网络接口的IP地址。不过,需要注意的是,现代Linux发行版可能推荐使用 ip 命令来替代 ifconfig,因为 ifconfig 在某些系统中可能已经被标记为过时或不再维护。如果你使用的是较新的Linux系统,建议考虑使用 ip 命令来提取IP地址。

取第一行

1
2
[root@localhost ~]# ifconfig | sed -n '/netmask/p' | sed 's/^.*inet //g' | sed 's/  netmask.*$//g' | sed -n '1p'
192.168.91.128
  • -n:这个选项告诉 sed 不要输出任何内容,除非明确指定。通常与 p 命令一起使用,以仅输出匹配的行。
  • '1p':这是一个 sed 脚本,它告诉 sed 打印(p)第一行(1)。

可以在末尾加g替换每一个匹配的关键字,否则只替换每行的第一个

另一种方式:

1
[root@localhost ~]# ip a|sed -n '/inet /p'|sed 's/^.*inet //g'|sed 's/\/.*$//g'|sed -n '2p'

这个命令组合使用 ip, sed 等命令来提取特定网络接口(通常是第二个网络接口)的IP地址。这里是对命令的逐步解释:

  1. ip a:

这个命令是 ip address 的简写,用于显示所有网络接口的地址信息。输出通常会包括接口的名称、IP地址、子网掩码、广播地址等。

  1. sed -n '/inet /p':

这个 sed 命令用于从 ip a 的输出中筛选出包含 “inet “ 的行。-n 选项告诉 sed 默认不输出任何内容,而 /inet /p 则指定只打印包含 “inet “ 的行。这通常用于提取IPv4地址。

  1. sed 's/^.\*inet //g':

这个 sed 命令用于处理上一步输出的行。s/^.*inet //g 是一个替换命令,它的作用是将每行中从开头到 “inet “ 的部分替换为空(即删除)。这用于从 ip a 的输出中提取出 “inet “ 后面的部分,通常是IP地址。

  1. sed 's/\/.\*$//g':

这个 sed 命令进一步处理上一步的输出。s/\/.*$//g 是一个替换命令,它的作用是将每行中从 “/“(子网掩码的开始)到行尾的部分替换为空(即删除)。这用于从IP地址和子网掩码的组合中只保留IP地址。

  1. sed -n '2p':

这个 sed 命令用于输出处理后的第二行。这通常用于提取第二个网络接口的IP地址,因为 ip a 的输出中,第一个网络接口(通常是 lo,即回环接口)通常位于列表的顶部。

综合以上步骤,整个命令组合的作用是提取系统中第二个网络接口的IPv4地址。

需要注意的是,这个命令组合假设 ip a 的输出中第二个包含 “inet “ 的行对应于第二个网络接口的IP地址。在大多数情况下,这是正确的,但如果系统配置不同,可能需要调整命令以适应特定情况。

多点编辑

一条sed命令,删除/etc/passwd第三行到末尾的数据,并把bash替换为blueshell

1
2
3
[root@localhost ~]# nl /etc/passwd | sed -e '3,$d' -e 's/bash/blueshell/'
1 root:x:0:0:root:/root:/bin/blueshell
2 bin:x:1:1:bin:/bin:/sbin/nologin

-e表示多点编辑,第一个编辑命令删除/etc/passwd第三行到末尾的数据,第二条命令搜索bash替换为blueshell。

注:这个命令组合做了几件事情。让我们逐步分析它:

  1. nl /etc/passwd

nl 命令用于给文件的每一行加上行号。在这里,它会给 /etc/passwd 文件的每一行加上行号。/etc/passwd 文件通常包含系统用户的账户信息。

  1. sed -e ‘3,$d’ -e ‘s/bash/blueshell/‘

sed 是一个流编辑器,用于对输入流(或文件)进行基本的文本转换。

-e 选项允许你指定一个或多个 sed 编辑命令。

  • '3,$d': 这个命令告诉 sed 删除从第3行到最后一行的所有内容。3,$ 是一个地址范围,其中 3 是起始行号,$ 表示最后一行。d 是一个命令,表示删除匹配的行。
  • 's/bash/blueshell/': 这是一个替换命令,它告诉 sed 将每一行中的第一个 “bash” 替换为 “blueshell”。

需要注意的是,这两个 sed 命令是顺序执行的。首先,所有从第3行到最后一行的内容都会被删除。然后,在剩下的内容中,所有的 “bash” 都会被替换为 “blueshell”。

因此,整个命令组合 nl /etc/passwd | sed -e '3,$d' -e 's/bash/blueshell/' 的作用是:

  1. /etc/passwd 文件的每一行加上行号。
  2. 删除从第3行到最后一行的所有内容。
  3. 在剩下的内容中,将所有的 “bash” 替换为 “blueshell”。

最终,你会看到 /etc/passwd 文件的前两行(带有行号),并且如果这两行中有 “bash” 的话,它会被替换为 “blueshell”。

直接修改文件内容(危险动作)

1
2
3
4
5
6
[root@localhost ~]# cat test 
line one.
line two.
line three.
line four.
line five.

加i参数就是直接修改

1
2
3
4
5
6
7
[root@localhost ~]# sed -i 's/ \.$ / \! /g' test 
[root@localhost ~]# cat test
line one!
line two!
line three!
line four!
line five!

注:这个 sed 命令做了以下事情:

  1. -i: 这个选项告诉 sed 直接修改输入文件,而不是将结果输出到标准输出(通常是终端)。
  2. 's/ \.$ / \! /g': 这是一个 sed 替换命令,其格式是 s/pattern/replacement/flags
    • \.: 匹配一个实际的点字符(.)。因为在正则表达式中,. 是一个特殊字符,表示匹配任何字符(除了换行符)。所以,为了匹配实际的点字符,我们需要使用反斜杠 \ 来转义它。
    • $: 匹配行的结尾。
    • \.$: 因此,\.$ 匹配以点字符结尾的行。
    • \!: 这是替换字符串,表示将匹配到的点字符替换为感叹号 !
    • g: 这是一个全局标志,表示替换每一行中所有匹配到的模式,而不仅仅是每一行的第一个匹配。

综上所述,这个 sed 命令会在文件 test 中查找所有以点字符结尾的行,并将这些点字符替换为感叹号。

例如,如果 test 文件的内容如下:

1
2
3
This is a line.  
Another line without a dot.
This line ends with a dot.

运行这个 sed 命令后,test 文件的内容将变为:

1
2
3
This is a line!  
Another line without a dot!
This line ends with a dot!

注意:使用 -i 选项时要小心,因为它会直接修改文件。如果你不确定结果是否正确,可以先不使用 -i 选项来查看结果,然后再决定是否直接修改文件。

\利用 sed 直接在最后一行加入 # test

1
2
3
4
5
6
7
8
[root@localhost ~]# sed -i '$a\# test' test 
[root@localhost ~]# cat test
line one!
line two!
line three!
line four!
line five!
# test

注:

  1. -i: 如之前所述,这个选项告诉 sed 直接修改输入文件,而不是将结果输出到标准输出。
  2. '$a\# test': 这是一个 sed 命令,用于在文件的最后一行之后追加文本。
    • $: 这是一个地址,表示文件的最后一行。
    • a: 这是一个命令,表示在指定的地址之后追加文本。
    • # test: 这是要追加的文本内容。

Linux三剑客之awk

使用方法

1
awk '{pattern + action}' {filenames}

其中 pattern 表示 AWK 在数据中查找的内容,而 action 是在找到匹配内容时所执行的一系列命令。花括号({})不需要在程序中始终出现,但它们用于根据特定的模式对一系列指令进行分组。 pattern就是要表示的正则表达式,用斜杠括起来。

awk语言的最基本功能是在文件或者字符串中基于指定规则浏览和抽取信息,awk抽取信息后,才能进行其他文本操作。完整的awk脚本通常用来格式化文本文件中的信息。

通常,awk是以文件的一行为处理单位的。awk每接收文件的一行,然后执行相应的命令,来处理文本。

awk 的原理

1
[root@localhost ~]# awk -F: '{print $0}' /etc/passwd

执行 awk 时,它依次对/etc/passwd 中的每一行执行 print 命令。

2

1
2
3
4
[root@localhost ~]# awk -F":" '{print $1}' /etc/passwd
[root@localhost ~]# awk -F":" '{print $1 $3}' /etc/passwd
[root@localhost ~]# awk -F":" '{print $1" " $3}' /etc/passwd
[root@localhost ~]# awk -F":" '{print "username:"$1"\t\tuid:" $3}' /etc/passwd

-F参数:指定分隔符,可指定一个或多个

print 后面做字符串的拼接

变量名 属性
$0 当前记录
$1~$n 当前记录的第n个字段
FS 输入字段分割符 默认是空格
RS 输入记录分割符 默认为换行符
NF 当前记录中的字段个数,就是有多少列
NR 已经读出的记录数,就是行号,从1开始
OFS 输出字段分割符 默认也是空格
ORS 输出的记录分割符 默认为换行符

注:内置变量很多,参阅相关资料

实例一:只查看test.txt文件(100行)内第20到第30行的内容(企业面试)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@localhost ~]# seq 100 > test.txt
[root@localhost ~]# awk '{if(NR>=20 && NR<=30) print $1}' test.txt
20
21
22
23
24
25
26
27
28
29
30
[root@localhost ~]# nl /etc/passwd|tail -n +20|head -n 10

实例二:已知test.txt文件内容为

1
2
[root@localhost ~]# cat test.txt 
I am aaron, my qq is 1234567

请从该文件中过滤出’aaron’字符串与1234567,最后输出的结果为:aaron 1234567

1
2
3
4
[root@localhost ~]# awk -F '[ ,]+' '{print $3" "$NF}' test.txt  #用[]可以放多个分隔符
aaron 1234567
等效
[root@localhost ~]# awk -F ',' '{print $1" "$2}' test.txt|awk '{print $3 " " $NF}'

BEGIN 和 END 模块

BEGIN只执行一次并且执行在主体代码块之前。

END只执行一次并且执行在主体代码块之后。

BEGIN可以抛开文件单独执行,结果类似于echo而END不可以。

BEGIN中没有文件的读取变量而END是有的。但END中的$0是awk处理到最后的文本样式。

实例一:统计/etc/passwd的账户人数

1
awk 'BEGIN {count=0;print "[start] user count is "count}{count++;print $0} END{print "[end] user count is "count}' /etc/passwd

count是自定义变量。之前的action{}里都是只有一个print,其实print只是一个语句,而action{}可以有多个语句,以;号隔开。这里没有初始化count,虽然默认是0,但是妥当的做法还是初始化为0

1
[root@localhost ~]# awk '{count++;print $0;} END{print "user count is "count}' /etc/passwd

实例二:统计某个文件夹下的文件占用的字节数

1
2
3
4
[root@localhost ~]# ll | awk 'BEGIN {size=0} {size=size+$5} END{print "size is ",size}'
size is 1468
[root@localhost ~]# ll | awk 'BEGIN {size=0} {size=size+$5} END{print "size is ",size/1024/1024,"M"}'
size is 0.00139999 M

awk运算符

运算符 描述
赋值运算符
= += -= *= /= %= ^= **= 赋值语句
逻辑运算符
\ \ 逻辑或
&& 逻辑与
正则运算符
~ !~ 匹配正则表达式和不匹配正则表达式
关系运算符
< <= > >= != == 关系运算符
算数运算符
+ - 加,减
* / & 乘,除与求余
+ - ! 一元加,减和逻辑非
^ *** 求幂
++ – 增加或减少,作为前缀或后缀
其他运算符
$ 字段引用
空格 字符串链接符
?: 三目运算符
in 数组中是否存在某键值

awk 赋值运算符:a+=5;等价于: a=a+5;其他同类

1
2
[root@node-1 ~]# awk 'BEGIN{a=5;a+=5;print a}'
10

awk逻辑运算符:判断表达式 a>2&&b>1为真还是为假,后面的表达式同理

1
2
[root@node-1 ~]# awk 'BEGIN{a=1;b=2;print (a>2&&b>1,a=1||b>1)}'
0 1

awk正则运算符:

1
2
[root@node-1 ~]# awk 'BEGIN{a="100testaa";if(a~/100/) {print "OK"}else {print "NO"}}'
OK

关系运算符:

如: > < 可以作为字符串比较,也可以用作数值比较,关键看操作数如果是字符串就会转换为字符串比较。两个都为数字才转为数值比较。字符串比较:按照ascii码顺序比较。

1
2
3
4
5
[root@node-1 ~]# awk 'BEGIN{a="11";if(a>=9){print"OK"}}'
[root@node-1 ~]# awk 'BEGIN{a=11;if(a>=9){print"OK"}}'
OK
[root@node-1 ~]# awk 'BEGIN{a;if(a>=b){print"OK"}}'
OK

awk 算术运算符:

说明,所有用作算术运算符进行操作,操作数自动转为数值,所有非数值都变为0。

1
2
3
4
[root@node-1 ~]# awk 'BEGIN{a="b";print a++,++a}'
0 2
[root@node-1 ~]# awk 'BEGIN{a="20b4";print a++,++a}'
20 22

这里的a++ , ++a与javascript语言一样:a++是先赋值加++;++a是先++再赋值

三目运算符 ?:

1
2
3
4
[root@node-1 ~]# awk 'BEGIN{a="b";print a=="b"?"ok":"err"}'
ok
[root@node-1 ~]# awk 'BEGIN{a="b";print a=="c"?"ok":"err"}'
err

常用 awk 内置变量

变量名 属性
$0 当前记录
$1~$n 当前记录的第n个字段
FS 输入字段分割符 默认是空格
RS 输入记录分割符 默认为换行符
NF 当前记录中的字段个数,就是有多少列
NR 已经读出的记录数,就是行号,从1开始
OFS 输出字段分割符 默认也是空格
ORS 输出的记录分割符 默认为换行符

注:内置变量很多,参阅相关资料

字段分隔符 FS

FS=”\t” 一个或多个 Tab 分隔

1
2
3
4
[root@node-1 ~]# cat tab.txt 
aa bb cc
[root@node-1 ~]# awk 'BEGIN{FS="\t+"}{print $1,$2,$3}' tab.txt
aa bb cc

FS=”[[:space:]+]” 一个或多个空白空格,默认的,匹配到不符合的就停止

1
2
3
4
[root@node-1 ~]# awk -F [[:space:]+] '{print $1,$2,$3,$4,$5}' tab.txt 
aa bb cc
[root@node-1 ~]# awk -F [[:space:]+] '{print $1,$2}' tab.txt
aa bb

FS=”[“ “]+” 以一个或多个空格或:分隔

1
2
[root@node-1 ~]# awk -F [" "]+ '{print $1,$2,$3}' hello.txt 
root x 0

字段数量 NF :显示满足用:分割,并且有8个字段的

1
2
[root@node-1 ~]# awk -F ":" 'NF==8{print $0}' hello.txt 
bin:x:1:1:bin:/bin:/sbin/nologin:888

记录数量 NR

1
2
[root@node-1 ~]# ifconfig ens33 | awk -F [" "]+ 'NR==2{print $3}'
192.168.175.10

RS 记录分隔符变量

将 FS 设置成”\n”告诉 awk, 每个字段都占据一行。通过将 RS 设置成””,还会告诉 awk每个地址记录都由空白行分隔。

1
2
3
4
5
6
7
8
9
10
[root@node-1 ~]# cat awk.txt 
#!/bin/awk
BEGIN {
FS="\n"
RS=""
}
{
print $1","$2","$3
}
[root@node-1 ~]# awk -f awk.txt recode.txt

在””分割符之内,符合\n分割的会被打印出来

OFS 输出字段分隔符

1
2
3
[root@node-1 ~]# awk 'BEGIN{FS=":";OFS="#"}{print $1,$2,$3}' hello.txt 
root#x#0
bin#x#1

ORS 输出记录分隔符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@node-1 ~]# cat awk.txt 
#!/bin/awk
BEGIN {
FS="\n"
RS=""
ORS="\n\n"
}
{
print $1","$2","$3
}
[root@node-1 ~]# awk -f awk.txt recode.txt
Jimmy the Weasel,100 Pleasant Drive,San Francisco,CA 123456

Big Tony,200 Incognito Ave.,Suburbia,WA 64890

awk正则

元字符 功能 示例 解释
^ 首航定位符 /^root/ 匹配所有以root开头的行
$ 行尾定位符 \ /root$/ 匹配所有以root结尾的行
. 匹配任意单个字符 /r..t/ 匹配字母r,然后两个任意字符,再以t结尾的行
* 匹配0个或多个前导字符(包括回车) /a*ool/ 匹配0个或多个a之后紧跟着ool的行,比如ool,aaaaool等
+ 匹配1个或多个前导字符 /a+b/ ab, aaab
? 匹配0个或1个前导字符 /a?b/ b,ab
[] 匹配指定字符组内的任意一个字符 /^[abc]/ 匹配以a或b或c开头的行
[^] 匹配不在指定字符组内任意一个字符 /^[^abc]/ 匹配不以字母a或b或c开头的行
() 子表达式组合 /(rool)+/ 表示一个或多个rool组合,当有一些字符需要组合时,使用括号括起来
\ 或者的意思 /(root)\ B/ 匹配root或者B的行
\ 转义字符 /a/// 匹配a//
,! 匹配,不匹配的条件语句 $1~/root/ 匹配第一个字段包含字符root的所有记录
x{m}x{m,}x{m,n} x重复m次x重复至少m次x重复至少m次,但是不超过n次 /(root){3}//(root){3,}//(root){3,6}/

正则应用

规则表达式

awk '/REG/{action} ' file ,/REG/为正则表达式,可以将$0 中,满足条件的记录送入到:action 进行处理

1
2
3
4
5
6
7
[root@node-1 ~]# awk '/root/{print$0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@node-1 ~]# awk -F ":" '$5~/root/{print$0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@node-1 ~]# ifconfig ens33 | awk 'BEGIN{FS="[[:space:]:]+"} NR==2{print$3}'
192.168.175.10

布尔表达式

awk '布尔表达式{action}' file 仅当对前面的布尔表达式求值为真时, awk 才执行代码块。

1
2
3
4
[root@node-1 ~]# awk -F: '$1=="root"{print$0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@node-1 ~]# awk -F: '($1=="root")&&($5=="root"){print$0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

awk 的 if、循环和数组

if

条件语句

awk 提供了非常好的类似于 C 语言的 if 语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
{
if ($1=="foo"){
if ($2=="foo"){
print"uno"
}else{
print"one"
}
}elseif($1=="bar"){
print "two"
}else{
print"three"
}
}

使用 if 语句还可以将代码:

1
! /matchme/ { print $1 $3 $4 }

转换成:

1
2
3
4
5
{
  if ( $0 !~ /matchme/ ) {
    print $1 $3 $4
  }
}

while

循环结构

我们已经看到了 awk 的 while 循环结构,它等同于相应的 C 语言 while 循环。 awk 还有”do…while”循环,它在代码块结尾处对条件求值,而不像标准 while 循环那样在开始处求值。

它类似于其它语言中的”repeat…until”循环。以下是一个示例:

do…while 示例

1
2
3
4
5
{
count=1 do {
print "I get printed at least once no matter what"
} while ( count !=1 )
}

与一般的 while 循环不同,由于在代码块之后对条件求值, “do…while”循环永远都至少执行一次。换句话说,当第一次遇到普通 while 循环时,如果条件为假,将永远不执行该循环。

for 循环

awk 允许创建 for 循环,它就象 while 循环,也等同于 C 语言的 for 循环:

1
2
3
for ( initial assignment; comparison; increment ) {
code block
}

以下是一个简短示例:

1
2
3
for ( x=1;x<=4;x++ ) {
print "iteration", x
}

break 和 continue

此外,如同 C 语言一样, awk 提供了 break 和 continue 语句。使用这些语句可以更好地控制 awk 的循环结构。

1
2
3
4
5
6
7
8
9
10
11
#!/bin/awk
BEGIN{
x=1
while(1) {
print "iteration",x
if ( x==10 ){
break
}
x++
}
}

continue 语句补充了 break

1
2
3
4
5
6
7
8
9
10
11
12
x=1
while (1) {
if ( x==4 ) {
x++
continue
}
print "iteration", x
if ( x>20 ) {
break
}
x++
}

continue在for中使用

1
2
3
4
5
6
7
8
9
#!/bin/awk
BEGIN{
for (x=1;x<=21;x++){
if (x==4){
continue
}
print "iteration",x
}
}

数组

AWK 中的数组都是关联数组,数字索引也会转变为字符串索引

在awk中,数组叫关联数组,与我们在其它编程语言中的数组有很大的区别。关联数组,简单来说,类似于python语言中的dict、java语言中的map,其下标不再局限于数值型,而可以是字符串,即下标为key,value=array[key]。竟然为key,那其下标也不再是有序的啦。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/awk
BEGIN{
cities[1]="beijing"
cities[2]="shanghai"
cities["three"]="guangzhou"
for( c in cities) {
print cities[c]
}
print cities[1]
print cities["1"]
print cities["three"]
}

awk -f awk.txt <filename>

用 awk 中查看服务器连接状态并汇总

1
2
3
[root@node-1 ~]# netstat -an|awk '/^tcp/{++s[$NF]}END{for(a in s)print a,s[a]}'
LISTEN 8
ESTABLISHED 1

常用字符串函数

3

字符串函数的应用

在 info 中查找满足正则表达式, /[0-9]+/ 用”!”替换,并且替换后的值,赋值给 info

1
2
[root@node-1 ~]# awk 'BEGIN{info="this is a test2010test!";gsub(/[0-9]+/,"!",info);print info}'
this is a test!test!
  1. BEGIN { … }: BEGIN 是一个特殊的 awk 模式,它在处理任何输入行之前执行。在这个 BEGIN 块中,你可以初始化变量或执行其他只需要执行一次的操作。
  2. info=”this is a test2010test!”: 这行代码在 BEGIN 块中初始化了一个名为 info 的变量,并给它赋了一个字符串值 "this is a test2010test!"
  3. gsub(/[0-9]+/,”!”,info): gsubawk 中的一个函数,用于在字符串中进行全局替换。这里,它查找 info 变量中所有的一个或多个连续数字(由正则表达式 [0-9]+ 定义),并将它们替换为感叹号 !

所以,这行代码将 info 中的所有数字替换为感叹号。

  1. print info: 这行代码打印出修改后的 info 变量的值。

如果查找到数字则匹配成功返回 ok,否则失败,返回未找到

1
2
[root@node-1 ~]# awk 'BEGIN{info="this is a test2010test!";print index(info,"test")?"ok":"no found";}'
ok
  1. BEGIN { … }: 正如之前提到的,BEGIN 是一个特殊的 awk 模式,它在处理任何输入行之前执行。
  2. info=”this is a test2010test!”: 这行代码在 BEGIN 块中初始化了一个名为 info 的变量,并给它赋了一个字符串值 "this is a test2010test!"
  3. index(info,”test”): indexawk 的一个内置函数,它返回子字符串在字符串中首次出现的位置(基于1的索引)。如果子字符串没有在字符串中找到,则返回0。在这个例子中,index(info,"test") 会返回 8,因为 “test” 首次出现在 info 变量的第8个字符位置。
  4. index(info,”test”)?”ok”:”no found”: 这是一个三元操作符,它基于 index 函数的返回值来决定输出什么。如果 index 返回非零值(即找到了子字符串),则输出 “ok”;否则输出 “no found”。
  5. print …: 这行代码打印出三元操作符的结果。

从第 4 个 字符开始,截取 10 个长度字符串

1
2
[root@node-1 ~]# awk 'BEGIN{info="this is a test2010test!";print substr(info,4,10);}'
s is a tes
  1. BEGIN { … }: 正如之前提到的,BEGIN 是一个特殊的 awk 模式,它在处理任何输入行之前执行。

  2. info=”this is a test2010test!”: 这行代码在 BEGIN 块中初始化了一个名为 info 的变量,并给它赋了一个字符串值 "this is a test2010test!"

  3. substr(info,4,10): substrawk 的一个内置函数,用于从字符串中提取子字符串。它接受三个参数:源字符串、起始位置和要提取的字符数。在这个例子中,它从 info 变量的第4个字符开始(基于1的索引),提取长度为10的子字符串。

    对于 info="this is a test2010test!",从第4个字符开始(即 ‘i’),提取10个字符得到的子字符串是 “s is a tes”。

  4. print substr(info,4,10): 这行代码打印出使用 substr 函数提取的子字符串。

分割 info,动态创建数组 tA,awk for …in 循环,是一个无序的循环。 并不是从数组下标1…n 开始

1
2
3
4
5
6
[root@node-1 ~]# awk 'BEGIN{info="this is a test";split(info,tA," ");print length(tA);for(k in tA){print k,tA[k];}}'
4
4 test
1 this
2 is
3 a
  1. BEGIN { … }: 如前所述,BEGIN 块在 awk 开始处理任何输入行之前执行。
  2. info=”this is a test”: 在 BEGIN 块中初始化一个名为 info 的变量,并给它赋了一个字符串值 "this is a test"
  3. split(info,tA,” “): split 函数用于将字符串 info 根据指定的分隔符(在这个例子中是空格 " ")分割成一个数组 tA。这样,tA[1] 将会是 "this"tA[2] 将会是 "is",以此类推。
  4. print length(tA): 打印数组 tA 的长度。由于字符串 "this is a test" 被空格分割成了四个部分,所以输出将是 4
  5. for(k in tA) { print k, tA[k] }: 这是一个 for 循环,用于遍历数组 tA。在 awk 中,当你遍历数组时,k 会自动被赋值为数组的索引(在这个例子中是数字),而 tA[k] 则是相应的值。这个循环会打印出数组的每一个索引和对应的值。
  6. 首先,它打印出数组的长度 4。然后,它遍历数组并打印出每个元素的索引和值。注意,在 awk 中,数组的索引默认是从 1 开始的,而不是从 0