目录

Linux文本处理工具

grep, sed, awk被称为linux文本处理三剑客,分别侧重于文本搜索流式编辑格式化文本

grep

  • 文本搜索工具

  • Schema:grep [option...] [patterns] [file...]

Options

短选项长选项作用
-e pattern--regexp=pattern指定搜索pattern,可以不显式指定
-f file--file=file从file中获取pattern进行搜索
-i/-yignore-case匹配忽略大小写
-v--invert-match匹配反选
-w--word-regexp指定单词完整匹配,其边界为行首或者除字母、数字、下划线以外的字符
-x--line-regexp行完整匹配
-c--count不打印匹配行,只打印匹配行数
-L -l--files-without-match --files-with-matches不打印匹配行,只打印完全不匹配/存在匹配的文件名
-m num--max-count=num指定最大匹配次数
-o--only-matching只打印完整的匹配内容,不打印匹配行
-q--quiet/silent不打印任何内容,只以返回码来描述是否存在匹配
-h -H--no-filename --with-filename不打印/打印匹配的文件名前缀,默认为打印(-H)
-n--line-number打印匹配的行号前缀
-A num--after-context=num打印匹配行,及其以后的num行
-B num--before-context=num打印匹配行,及其以前的num行
-C num-context=num/-num打印匹配行,及其前后的各num行
-R--dereference-recursive递归的匹配目录下所有文件,遇到符号链接正常递归

Tips

  • grep不支持换行符的匹配

sed

  • 流编辑器,用于逐行地对输入流进行基本的文本转换

  • schema:sed SCRIPT INPUTFILE

    • INPUTFILE未指定或者指定为-时,表示从stdin读取内容
    • 默认输出为stdout,可以通过w cmd/重定向符保存到文件,可以指定-i在文件内原地修改
  • 多个cmd可以通过;或换行来指定,或者-e指定。(a,c,i命令中不应使用;,会将其当做plain text)

  • data buffer

    • 模式空间(Pattern space):数据处理空间,sed读取每行到模式空间,进行地址匹配并执行命令
    • 保持空间(Hold space):辅助暂存空间,在复杂处理过程中,作为数据的暂存区域

Options

短选项长选项作用
-n--quiet/--silent禁掉打印模式空间的内容,一般结合p命令打印处理过的行
-f--file指定sed脚本文件路径,命令形式指定则对应选项-e,一般无需显式指定-e
-i--in-place=[suffix]原地修改文件,如指定suffix会拼接到原文件名作为原文件内容的backup
-E/-r--regexp-entended使用扩展正则表达式
-s--seperate指定操作多个独立的文件
--posix严格限定posix,禁用GNU扩展,便于简化可移植脚本的编写

Commands

基本形式:[addr]X[options],其中addr为地址匹配模式,X为单字符命令,options指一些命令需要的额外选项。

  • s命令:替换字符串
    • s/regexp/replacement/flags
    • 根据regexp去模式空间找匹配,匹配成功后用replacement去替换掉regexp
    • 特殊符号需插入\转义
    • 常用flag:
      • g:所有匹配均替换
      • number:只替换第number个匹配
      • p:如果发生匹配,打印替换后的模式空间
      • i/I:匹配忽略字母大小写
      • m/M:支持多行匹配
  • 其他常见命令
cmd作用
d删除模式空间内容,并开始下一轮匹配
p匹配成功时,打印模式空间,一般配合-n选项使用
n读取下一行替换当前模式空间的行,执行下一条处理命令而非第一条命令。一般用于周期性替换,如偶数行替换 seq 6 | sed 'n;s/./x/'N命令在此基础上支持换行处理
{}封装一组命令用以执行
a text在一行后添加text
i text在一行前插入text
c text使用text替换行
y/source-chars/dest-chars执行字符的替换,如使用0-9替换a-jecho hello | sed 'y/abcdefghij/0123456789/'
r filename读取文件
w filename将模式空间内容写入到文件
b label类似于if-else的分支命令,goto label,如跳过第一行的替换:printf '%s\n' a1 a2 a3 | sed -E '/1/bx ; s/a/z/ ; :x'
  • 与保持空间相关的命令
cmd作用
g使用保持空间的内容替换模式空间的内容
G添加一个新行,并将保持空间的内容追加到模式空间
h使用模式空间的内容替换保持空间的内容
H添加一个新行,并将模式空间的内容追加到保持空间
x交换保持空间和模式空间的内容

地址匹配

  • 行号匹配
    • number:第几行
    • $: 最后一行
    • first~step:从first开始,每隔step匹配
  • 正则匹配
  • 范围匹配
    • addr1,+N:匹配[addr1, addr1+N]
    • addr1,~N:匹配[addr1, addr1+addr1%N]
1
2
3
4
# examples
sed '4,17s/hello/world/' input.txt # 第4-17行将所有hello替换成world
sed '/apple/s/hello/world/' input.txt # 所有包含apple的行将hello替换为world
sed '2!s/hello/world/' input.txt > output.txt # 除第二行以外将所有hello替换为world

Tips

  • -i可以指定可选参数,所以其后不应有其他的短选项

    • sed -Ei ... => sed -E -i ...
    • sed -iE ... => sed --in-place=E ...
  • label用作循环处理文本,一般会配合n/N使用,如多行合并的示例:seq 6 | sed ':x; N;s/\n//; bx;'

  • D,G,H,N,P支持多行处理,每个命令的作用与其小写命令相同

awk

文本格式化工具,多用于格式化文本,生成报表。

基本用法

schema

1
awk [options] 'Pattern{Action}' file

普通格式化

1
2
# $0表示完整行,$1表示第一列,$NF表示最后一列,$(NF-1)表示倒数第二列
df | awk '{print $1, $2}'

设置分隔符

1
2
3
4
5
# 输入字段分隔符,可以使用-F: 或者-v FS=:的形式设置
cat /etc/passwd | awk -F: '{print $1}

# 输出字段分隔符,可以使用-v OFS=@的形式设置
cat /etc/passwd | awk -v FS=: -v OFS=@ '{print $1, $3}'

内置变量

变量名作用默认值
FS输入字段分隔符空格
OFS输出字段分隔符空格
RS输入换行符(记录分隔符)\n
ORS输出换行符\n
NF字段数目
NR行号
FNR文件序号
FILENAME当前文件名
ARGC命令行参数的个数
ARGV命令行参数组成的数组
1
2
# 打印行号,变量在action里不应加$,加$表示获取对应列的内容
df | awk '{print NR, $2}'

自定义变量

1
2
3
4
# 使用-v设置
awk -v a="b" 'BEGIN {print a}'
# 在action中设置,并用分号分隔
awk 'BEGIN {a="b"; print a}'

特殊模式

  • BEGIN表示在处理文本前执行action,可用于打印表头
  • END表示在处理文本后执行action,可用于打印表尾
1
df | awk 'BEGIN{print "开始时执行"} {print $1} END{print "结束时执行"}'

条件匹配

1
2
3
4
5
# 打印第2行,第一列的内容
df | awk 'NR == 2 {print $1}'

# 如果第3行的值大于0,打印完整行
df | awk '$3>0 {print}'

正则相关

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 打印/dev开头的行
# 正则模式采用awk '/regexp1/{action}'的形式
df | awk '/^\/dev/{print}' 

# 打印最后一列以/dev开头的行
# 正则匹配采用awk 'var~/regexp1/{action}'的形式
df | awk '$NF~/^\/dev/{print}'

# 打印最后一列从/d开头到/p开头之间的行
# 范围模式采用 awk '/regexp1/, /regexp2/ {action}'形式
df | awk '$NF~/^\/d/,$NF~/^\/p/{print}'

分支动作

1
2
3
4
5
6
# 判断分支
df | awk '{ if($2>0){print "大于0"}else{print "小于0"} }'

# 循环分支
df | awk '{ for(i=1; i<3; i++){print $i}}'
df | awk '{ i=0; while(i<4){print $0; i++}}' # 将每行打印4遍

Tips

  • {}意味着代码块,可以通过;将语句划归到同一代码块中
  • print动作如果使用,分隔两列,则输出会以输出字段分隔符将两列连接起来,如果以空格分隔,则输出会将两列直接拼接打印
  • awk支持三元运算符

参考文档