# grep

grep 是文本查找命令,可以通过正则查找匹配。

1
2
3
4
5
6
7
8
9
10
11
$ grep 'root' passwd
root:x:0:0:root:/root:/bin/bash


$ grep '^\w\{4\}:' passwd
root:x:0:0:root:/root:/bin/bash
sync:x:4:65534:sync:/bin:/bin/sync
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin

# sed

流处理编辑器, sed 一次处理一行内容,不改变文件内容(除非重定向)。

  1. 文本或管道输入
  2. 读入一行到模式空间(临时缓存区)
  3. sed 命令处理
  4. 输出到屏幕
  5. 重复 2

# sed 格式

  • 命令行格式

    sed [options] 'command' file(s)

    options:

    • -n: -n, --quiet, --silent suppress automatic printing of pattern space
    • -e: -e script, --expression=script add the script to the commands to be executed

    command: 行定位(正则) + sed 命令(操作)

  • 脚本格式

    sed -f scriptfile file(s)

# p 打印

sed 读入一行自动输出打印一行,p 打印命令也会打印一行,所以 sed 'p' passwd 每行会打印两遍。使用 -n option 关闭自动打印。

1
2
3
4
5
6
7
8
9
10
11
$ sed 'p' passwd
root:x:0:0:root:/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
......

$ sed -n 'p' passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
......

行定位

定位一行:行号 x , 正则表达式 /pattern/

定位间隔行:行号 x,y , 正则表达式 /pattern1/,/pattern2/ , 混合 /pattern/,x

取反: x,y!

定位间隔几行: first ~ step

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
$ nl passwd | sed -n '10p'
10 news:x:9:9:news:/var/spool/news:/usr/sbin/nologin

$ nl passwd | sed -n '/news/p'
10 news:x:9:9:news:/var/spool/news:/usr/sbin/nologin

$ nl passwd | sed -n '1,2p'
1 root:x:0:0:root:/root:/bin/bash
2 daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

$ nl passwd | sed -n '/root/,/daemon/p'
1 root:x:0:0:root:/root:/bin/bash
2 daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

$ nl passwd | sed -n '1,/daemon/p'
1 root:x:0:0:root:/root:/bin/bash
2 daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin

$ nl passwd | sed -n '1,/daemon/!p'
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
6 games:x:5:60:games:/usr/games:/usr/sbin/nologin

$ nl passwd | sed -n '1~2p'
1 root:x:0:0:root:/root:/bin/bash
3 bin:x:2:2:bin:/bin:/usr/sbin/nologin
5 sync:x:4:65534:sync:/bin:/bin/sync
7 man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
9 mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
......

# a、i、c、d 行处理

  • a 新增行 (之后) i 插入行 (之前)
  • c 替代行 sed '1,3 c ==========' 范围替换时整体替换,不是单行替换, 1-3 行都替换为 ==========
  • d 删除行
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
$ nl passwd | sed '2a =========='
1 root:x:0:0:root:/root:/bin/bash
2 daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
==========
3 bin:x:2:2:bin:/bin:/usr/sbin/nologin
......

$ nl passwd | sed '2,3a =========='
1 root:x:0:0:root:/root:/bin/bash
2 daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
==========
3 bin:x:2:2:bin:/bin:/usr/sbin/nologin
==========
4 sys:x:3:3:sys:/dev:/usr/sbin/nologin
......

$ nl passwd | sed '2i =========='
1 root:x:0:0:root:/root:/bin/bash
==========
2 daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
3 bin:x:2:2:bin:/bin:/usr/sbin/nologin
......


$ nl passwd | sed '1c =========='
==========
2 daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
3 bin:x:2:2:bin:/bin:/usr/sbin/nologin
......


$ nl passwd | sed '1,3c =========='
==========
4 sys:x:3:3:sys:/dev:/usr/sbin/nologin
5 sync:x:4:65534:sync:/bin:/bin/sync
......

$ nl passwd | sed '1d'
2 daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
3 bin:x:2:2:bin:/bin:/usr/sbin/nologin
......

$ nl passwd | sed '/root/d'
2 daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
3 bin:x:2:2:bin:/bin:/usr/sbin/nologin
......

优化服务器配置,在 ssh 的配置 ssh_config 文件中加入相应的文本。

1
2
#   port123
# permitrootlogin no

因为命令和文本使用空格分隔,如果在开头使用空格需要添加转义符。

1
2
3
4
5
6
7
8
9
10
11
12
$ sed '$a    port456 \n    permitrootlogin no' ssh_config
# port123
# permitrootlogin no
port456
permitrootlogin n


$ sed '$a \ port456 \n permitrootlogin no' ssh_config
# port123
# permitrootlogin no
port456
permitrootlogin no

删除文本中的空行。echo -e 表示启用解释反斜杠转义。

1
2
3
4
$ echo -e 'a\n\nb\n\nc' | sed '/^$/d'
a
b
c

# s 替换命令

1
2
3
4
5
6
7
8
9
10
11
$ sed 's/:/%/' 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
......

$ sed 's/:/%/g' 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
......

# {} 多个 sed 命令,用;分开

1
2
3
4
5
6
7
8
9
10
11
$ nl passwd | sed '{1,3d;s/:/%/g}'
4 sys%x%3%3%sys%/dev%/usr/sbin/nologin
5 sync%x%4%65534%sync%/bin%/bin/sync
6 games%x%5%60%games%/usr/games%/usr/sbin/nologin
......

$ nl passwd | sed -n '{p;n}'
1 root:x:0:0:root:/root:/bin/bash
3 bin:x:2:2:bin:/bin:/usr/sbin/nologin
5 sync:x:4:65534:sync:/bin:/bin/sync
......

# n 读取下一个输入行(用下一个命令处理)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ nl passwd | sed -n '{n;p}'
2 daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
4 sys:x:3:3:sys:/dev:/usr/sbin/nologin
6 games:x:5:60:games:/usr/games:/usr/sbin/nologin
......


$ nl passwd | sed -n '{n;n;p}'
3 bin:x:2:2:bin:/bin:/usr/sbin/nologin
6 games:x:5:60:games:/usr/games:/usr/sbin/nologin
9 mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
......


$ nl passwd | sed -n '1~2p'
1 root:x:0:0:root:/root:/bin/bash
3 bin:x:2:2:bin:/bin:/usr/sbin/nologin
5 sync:x:4:65534:sync:/bin:/bin/sync
......

# & 替换固定字符串

1
2
3
4
5
$ sed 's/^[a-z_-]\+/&   /'  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
......

大小写转换 \u\l\U\L

1
2
3
4
5
6
7
8
9
10
11
$ sed 's/^[a-z_-]\+/\u&/'  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
......

$ sed 's/^[a-z_-]\+/\U&/' 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
......

将文件夹下的 .txt 文件名转换为大写

1
2
3
4
$ ls *.txt | sed 's/^\w\+/\U&/'
123.txt
ABC.txt
TEST.txt

# () 匹配组

获取 passwd 中 USER、UID 和 GID.

1
2
3
4
5
6
$ sed 's/\(^[a-z_-]\+\):x:\([0-9]\+\):\([0-9]\+\).*$/USER:\1    UID:\2    GID:\3/' passwd
USER:root UID:0 GID:0
USER:daemon UID:1 GID:1
USER:bin UID:2 GID:2
USER:sys UID:3 GID:3
USER:sync UID:4 GID:65534

# rw 读写

r: 复制指定文件插入到匹配行,不改变文件内容。

w: 复制指定行拷贝到指定文件里,会改变目标文件内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ echo -e '123\n456\n789' > 123.txt
$ echo -e 'abc\ndef\nghk' > abc.txt

$ sed '1r 123.txt' abc.txt
abc
123
456
789
def
ghk


$ sed '1w abc.txt' 123.txt
123
456
789

$ cat abc.txt
123

# q 退出 sed

1
2
3
4
$ nl passwd | sed '3q'
1 root:x:0:0:root:/root:/bin/bash
2 daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
3 bin:x:2:2:bin:/bin:/usr/sbin/nologin

# awk

文本与数据处理工具,与 sed 相比可以编程,处理灵活,功能强大。更侧重于复杂逻辑处理。

# awk 处理方式与格式

# awk 处理方式

  • awk 一次处理一行内容。
  • awk 对每行可以切片处理。

awk '{print $1}' 输出首个单词。

# awk 格式

  • 命令行格式

    awk [options] 'command' file(s)

    基本格式: command: pattern {awk 操作命令}

    pattern: 正则表达式;逻辑判断式。

    awk 操作命令:内置函数 print (), printf (), getline…; 控制指令:if (){} else {}, while () {}。

    扩展格式:command: BEGIN {print "start"} pattern {awk 操作命令} END {print "end"}

  • 脚本格式

    awk -f awk-script-file file(s)

# awk 内置参数

  • awk 内置变量 1

    $0: 表示整个当前行

    $1: 每行第一个字段

    $2: 每行第二个字段

  • awk 内置参数:分隔符

    options: -F field-separator (默认为空格)

    例如: awk -F ':' '{print $3}' /etc/passwd

    1
    2
    3
    4
    5
    6
    7
    8
    $ echo 'babb:28:170' | awk -F ':' '{print $1,$2}'
    babb 28

    $ echo 'babb:28:170' | awk -F ':' '{print $1" "$2}'
    babb 28

    $ echo 'babb:28:170' | awk -F ':' '{print "name:" $1 "\t" "age:" $2}'
    name:babb age:28
  • awk 内置变量 2

    NR: 每行的记录号

    NF: 字段数量的变量

    FILENAME: 正在处理的文件名

    例如: awk -F ':' '{print FILENAME,NR,NF}' /etc/passwd

    显示 /etc/passwd 每行的行号,每行的列数,对应的用户名(prrint, printf)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    $ awk -F ':' '{print "Line: "NR, "Col: "NF, "User: "$1}' passwd
    Line: 1 Col: 7 User: root
    Line: 2 Col: 7 User: daemon
    Line: 3 Col: 7 User: bin
    ......

    $ awk -F ':' '{printf("Line: %s Col: %s User: %s\n",NR,NF,$1)}' passwd
    Line: 1 Col: 7 User: root
    Line: 2 Col: 7 User: daemon
    Line: 3 Col: 7 User: bin
    ......

    $ awk -F ':' '{printf("Line: %3s Col: %s User: %s\n",NR,NF,$1)}' passwd
    Line: 1 Col: 7 User: root
    Line: 2 Col: 7 User: daemon
    Line: 3 Col: 7 User: bin
    ......
    Line: 9 Col: 7 User: mail
    Line: 10 Col: 7 User: news
    Line: 11 Col: 7 User: uucp
    ......

显示 /etc/passwd 中用户 ID 大于 100 的行号和用户名(if … else …)

1
2
3
4
5
$ awk -F ':' '{if ($3>100) print "Line: "NR, "User: "$1}' passwd
Line: 18 User: nobody
Line: 20 User: syslog
Line: 21 User: messagebus
......

在服务器 log 中找出 ‘Error’ 的发生日期

1
2
3
4
5
6
7
8
9
$ sed -n '/Error/p' fresh.log | awk '{print $1}'
2016-11-01T10:39:04.09558300+08:00
2016-11-02T10:39:04.09558300+08:00
2016-11-03T08:39:04.09558300+08:00

$ awk '/Error/{print $1}' fresh.log
2016-11-01T10:39:04.09558300+08:00
2016-11-02T10:39:04.09558300+08:00
2016-11-03T08:39:04.09558300+08:00

# awk 逻辑判断式

~, !~ : 匹配正则表达式。

1
2
3
4
5
6
7
8
9
10
11
12
$ awk -F ':' '$1~/^m.*/{print $1}' passwd
man
mail
messagebus
myths

$ awk -F ':' '$1!~/^m.*/{print $1}' passwd
root
daemon
bin
sys
......

==, !=, <, > : 判断逻辑表达式。

1
2
3
4
5
$ awk -F ':' '$3>100 {print $1}' passwd
nobody
syslog
messagebus
usbmux

# awk 扩展格式

扩展格式:command: BEGIN {print "start"} pattern {awk 操作命令} END {print "end"} 可以在整个循环开始和结尾做一些操作。

制表显示 /etc/passwd 每行的行号,每行的列数,对应的用户名。

1
2
3
4
5
6
7
8
9
$ awk -F ':' 'BEGIN {print "Line\tCol\t User"}{print NR"\t"NF"\t"$1}END{print "----------"FILENAME"----------"}' passwd
Line Col User
1 7 root
2 7 daemon
3 7 bin
4 7 sys
5 7 sync
......
----------passwd----------

统计当前文件夹下的文件 / 文件夹占用的大小。

1
2
3
4
5
6
7
8
9
10
11
12
$ ls -l
total 12
-rw-r--r-- 1 11435 1049089 24 3月 14 16:00 123.txt
-rw-r--r-- 1 11435 1049089 22 3月 14 16:04 abc.txt
-rw-r--r-- 1 11435 1049089 531 3月 14 17:21 fresh.log
-rw-r--r-- 1 11435 1049089 2463 3月 11 14:02 index.html
-rw-r--r-- 1 11435 1049089 2202 3月 14 16:55 passwd
-rw-r--r-- 1 11435 1049089 127 3月 14 15:57 ssh_config


$ ls -l | awk 'BEGIN{size=0}{size+=$5}END{print " size is " size}'
size is 5369

统计显示 /etc/passwd 的账户总人数。

1
2
$ awk -F ':' 'BEGIN{count=0}$1!~/^$/{count++}END{print " count = " count}' passwd
count = 17

统计显示 UID 大于 10 的用户名。

1
2
3
4
5
6
7
$ awk -F ':' 'BEGIN{count=0}{if ($3>10) name[count++]=$1}END{for (i=0;i<count;i++) print i, name[i]}' passwd
0 proxy
1 www-data
2 backup
3 list
4 irc
5 gnats

统计 netstat -anp 状态下为 LISTEN 和 CONNECTED 的连接数量。

! 需要在 Linux 下测试

1
$ netstat -anp | awk '$6~/CONNECTED|LISTEN/{sum[$6]++}END{for(i in sum) print i, sum[i]}'

# etc/passwd 文件样例

用户名:口令:用户标识号:组标识号:注释性描述:主目录:登录 Shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System

# fresh.log 文件样例

1
2
3
4
5
6
7
8
2016-10-31T10:39:04.09558300+08:00 Fresh token in 7199 seconds later...
2016-10-31T12:39:04.09558300+08:00 Fresh token in 7199 seconds later...
2016-11-01T10:39:04.09558300+08:00 Fresh token in 7199 seconds later...
2016-11-01T10:39:04.09558300+08:00 Error in genToken()
2016-11-01T12:39:04.09558300+08:00 Fresh token in 7199 seconds later...
2016-11-01T20:39:04.09558300+08:00 Fresh token in 7199 seconds later...
2016-11-02T10:39:04.09558300+08:00 Error in genToken()
2016-11-03T08:39:04.09558300+08:00 Error in genToken()

# 应用

# 查找不包含 TEST 的文件夹

-v, --invert-match: select non-matching lines

1
2
3
$ ls | grep -v '.*\?TEST.*\?'

$ ls | awk '!/TEST/'

# 批量替换文件名

src dest
AAA-o60807012@76027.xml o60807012.out
BBB-o60466251@75842.xml o60466251.out
1
2
3
4
5
6
$ ls *.xml | sed 's/.*\?-\(.*\?\)@.*\?/mv & \1.out/'
mv AAA-o60807012@76027.xml o60807012.out
mv BBB-o60466251@75842.xml o60466251.out

# 需要添加 sh 才会执行 shell 命令
$ ls *.xml | sed 's/.*\?-\(.*\?\)@.*\?/mv & \1.out/' | sh
Edited on