[TOC]
正则表达式与文本搜索
元字符
.
匹配任意单个字符(单行模式下不匹配换行符)*
匹配前一个字符任意次[]
匹配范围内任意一个字符^
匹配开头$
匹配结尾\
转义后面的特殊字符
扩展元字符
+
先前的项可以匹配一次或多次。?
先前的项是可选的,最多匹配一次。|
匹配前面或后面的正则表达式, "或"()
分组
重复
一个正则表达式后面可以跟随多种重复操作符之一。
{n} 先前的项将匹配恰好 n 次。
{n,} 先前的项可以匹配 n 或更多次。
{n,m} 先前的项将匹配至少 n 词,但是不会超过 m 次
find 命令
递归地在目录中查找文件
find [路径...] 表达式
表达式
查找范围
-maxdepth <level> # 目录递归最大层数
-mindepth <level> # ?
按文件名查找
-name "模式" # 完整匹配基本的文件名, 使用"通配符"匹配基本的文件名
-regex "模式" # 完整匹配整个路径(并非单单匹配文件名), 使用"正则"匹配基本的文件名
-iregex "模式" # 完整匹配整个路径(并非单单匹配文件名) , 使用"正则"匹配基本的文件名, 但不区分大小写
-regextype <reg_type> # 改变正则表达式语法, 可选: emacs (this is the default), posix-awk, posix-basic, posix-egrep and posix-extended
按文件类型查找
-type 类型 # b 块设备, c 字符设备, d 目录, p 命名管道, f 普通文件, l 符号链接, s 套接字
按时间查找
# 数字参数
# +n 在这之前的
# -n 在这之后的
# n 正好处于该时刻
-daystart # 从当日0点为基准, 而不是当前时刻, 影响下述几种时间类型.
-atime <n> # Access, 最后访问时间, 单位为天(但实际是以当前时间为基准)
-ctime <n> # Change, 文件i节点修改时间, 单位为天
-mtime <n> # Modify, 文件内容修改时间, 单位为天
-amin <n> # 类似 atime, 但单位是分钟
-ctim <n> # 类似 ctime, 但单位是分钟
-mmin <n> # 类似 mtime, 但单位是分钟
按大小
-size <n> # 默认单位是块(512字节), 支持 k(KB), M(MB), G(GB), 可以用 + - 或无符号, 意义同上面的按时间查找.
按归属
-user <user> # 按属主
-uid <uid> # 按属主的id
动作
-exec 操作 \; # 执行时无需确认, {} 作为转义字符会被替换成查找的文件
-ok 操作 \; # 类似 -exec, 但是每次操作都会提示确认
运算符(按优先级从高到低排序)
() # 强制优先
! <表达式> # 逻辑非, 对<表达式>结果取反, 即不匹配后面条件, 比如 ! -name 表示不匹配指定文件名
-not <表达式> # 逻辑非, 同 ! <表达式>
<表达式1> <表达式2> # 逻辑与(默认), 如果前一个<表达式>执行结果为false, 则不会执行后续<表达式>
<表达式1> -a <表达式2> # 逻辑与, 同上
<表达式1> -and <表达式2> # 逻辑与, 同上
<表达式1> -o <表达式2> # 逻辑或
<表达式1> -or <表达式2> # 逻辑或
<表达式1> , <表达式2> # 前一个表达式的结果不影响后一个表达式的执行
cat
仅会修改 access 时间
touch
会同时修改 access, modify, change
chmod
仅会修改 change 时间
注意:
- 不同参数的前后顺序很重要, 比如
-daystart
需要写在-atime
等之前, 否则对其不生效.
示例
# 仅删除昨天的日志
find /path/to/log -daystart -mtime 1 -exec rm -v {} \;
grep 命令
文本内容过滤(查找)
查找文本中具有关键字的一行
说明
若未提供查找的文件名或是 - 则默认是标准输入
语法
grep [选项] 模式 文件...
grep [选项] (-e 模式 | -f 包含模式的文件) 文件...
选项
模式
-G, --basic-regexp # 使用基本正则(默认)
-E, --extended-regexp # 使用扩展正则
-e 模式, --regexp=模式 # 当模式以 - 开头时, 应使用这种方式
-v, --invert-match # 反向匹配, 只选择不匹配的行
-i, --ignore-case # 忽略大小写
-R, -r, --recursive # 递归地读每一目录下的所有文件。这样做和 -d recurse 选项等价。
显示内容
-A <n>, --after-context=<n> # 打印匹配行的后续 n 行
-B <n>, --before-context=<n> # 打印匹配行的前面 n 行
-o, --only-matching # 只显示匹配的部分(默认是显示匹配行的所有内容)
-n, --line-number # 同时显示行号
修改显示类型, 不进行通常输出
-c # 打印匹配到多少行
-l, --files-with-matches # 打印匹配的文件名
-L, --files-without-match # 打印不匹配的文件名
注意:
- 在输入选项时尽量使用引号包围, 避免Shell去解释, 比如
grep \.
实际上模式是.
也就是匹配任意字符. 而grep "\."
或grep '\.'
才是匹配模式\.
在基本正则表达式中,元字符 ?, +, {, |, (, 和 ) 丧失了它们的特殊意义;作为替代,使用加反斜杠的 (backslash) 版本 ?, +, {, |, (, 和 ) 。
cut 行切割
在文件的每一行提取片段
cut 选项 [FILE]...
选项
-d, --delimiter <分隔> # 以指定分隔符来切割, 分隔符必须是单个字符
-f, --fields <list> # 输出指定位置的字段, 用逗号分隔多个位置, 位置从1开始
uniq 连续重复行处理
删除排序文件中的"连续"重复行
默认从标准输入读取, 输出到标准输出
uniq 选项 [INPUT [OUTPUT]]
选项
-c, --count # 在首列显示重复数量
-d, --repeated # 仅显示重复的行
sort 排序
对文本文件的行排序
sort 选项 [FILE]...
选项
字段类型
-n # 按照数值排序, 默认包含 -b
-k #
-r # 逆向排序
-b # 忽略开头的空格
seq 产生数字序列
产生数字序列
语法
seq [OPTION]... LAST
seq [OPTION]... FIRST LAST
seq [OPTION]... FIRST INCREMENT LAST
tac 倒序显示
tac [选项] <文件=STDIN>
行编辑器
非交互式, 基于行操作的模式编辑.
sed 行编辑器
sed 是单行文本编辑器, 非交互式.
sed 的模式空间, 其基本工作方式
- 将文件以行为单位读取到内存(称作模式空间)
- 使用sed的每个脚本依次对该行进行操作
- 打印模式空间的内容并清空
- 读取下一行, 重复执行上述步骤
sed 的空间示意图:
- 模式空间的内容默认会输出, 并清空
- 保持空间的默认内容是
\n
Tip
- 使用
;
可以替换多个-e
模式空间 pattern space
s 替换命令
替换
sed [选项] '[<寻址>]s<分隔符><old><分隔符><new><分隔符>[<标志位>]' [文件...] # 简单示例: sed 's/old/new/'
参数
old # 支持正则表达式
分隔符 # 可以采用 / 也可以采用其他来避免与正则匹配内容冲突, 比如 ~ @ 等
寻址(默认是所有行)
! # 对寻址取反, eg. "2,4!d" 表示不删除2~4行
<n> # 只替换第<n>行
<n1>,<n2> # 区间: 从<n1>到<n2>这几行 eg. /12\/Apr\/2020/,/13\/Apr\/2020/ 正则同样可以使用这种区间寻址
1,<n> # 替换从开始到第<n>行
<n>,$ # 替换从第<n>到结束的这些行
/正则/ # 仅替换符合此正则匹配到的行
# eg. sed '/正则/s/old/new/'
# eg. sed '/正则/,$s/old/new/' (正则可以和行号混用)表示从匹配到的正则那行开始到结束都替换
# 寻址可以匹配多条命令, 注意大括号. eg. /regular/{s/old/new/;s/old/new/}
# 比如nginx日志需要筛选出 14号这天的: sed -n '/14\/Apr\/2020/,/15\/Apr\/2020/ p' xx.log
标志位(默认是只替换第1个匹配项)
/g # 替换所有匹配项(默认只替换第1个匹配项)
/<n> # <n>是一个数字, 表示每行只替换第<n>个匹配项
/p # 打印模式空间的内容(即匹配的行), 通常会和 -n 一起使用, 如果不和 -n 一起使用, 会导致匹配的行多输出依次.
# eg. sed -n 's/old/new/p' # 此时仅打印匹配的行
/w <file> # 将模式空间的内容(即匹配到的行)写入到指定文件
选项
-r, --regexp-extended # 使用扩展正则表达式, 包括圆括号分组及回调.
-e script, --expression=script # 指定多个执行脚本时, 每个脚本前用一个 -e. 可以使用 "分号" 简写
# Eg. -e 's/old1/new1/' -e 's/old2/new2/'
-f script-file, --file=script-file # 加载脚本文件
-i[<后缀>], --in-place[=<后缀>] # 修改原始文件, 当指定后缀时则会生成对应的备份文件. 也可以直接输出重定向输出到其他文件
-n, --quiet, --silent # 默认不自动打印
示例
# 使用扩展正则表达式
sed -r 's/old/new/' [文件]...
# 执行多个脚本
sed -e 's/old/new/' -e 's/old/new/' [文件]...
sed 's/old/new/;s/old/new/' # 使用分号隔开不同脚本
# 将结果写回文件保存
sed -i 's/'
sed -i,.bak 's/'
# 圆括号分组及回调
echo "a213123t" | sed -r 's/a(\d*)/b\1/g' # b213123t
d 删除命令
删除当前"模式空间的内容", 并放弃后面的命令, 读取新的内容并重新执行sed
改变脚本的控制流, 读取新的输入行
(由于模式空间的内容被删除了, 因此d后面的脚本没法执行, 会略过)
(使用 s 替换成空内容, 但本质上这一行内容还在, 依旧会执行后续脚本, 会输出)
sed '[<寻址>]d' [文件...]
寻址
同 s 命令
示例
sed '1d' # 删除第一行
sed '/^\s*#/d' # 删除 # 开头的行
a 追加命令
在匹配行的下一行插入内容
sed '[<寻址>]a <插入内容>'
示例
sed '1i haha' # 在原先第1行前面插入 haha
i 插入命令
在匹配行在上一行插入内容
sed '[<寻址>]i <插入内容>'
示例
sed '2i haha' # 在原先第二行前面插入 haha
c 改写命令
将匹配行替换成指定内容
指定匹配连续几行时, 只会替换1次 # sed '2,5c <替换内容>'
sed '[<寻址>]c <替换内容>'
示例
sed '2c hehe' # 将第2行替换成 "hehe"
r 读文件并插入 (从文件读取改写内容)
在匹配行下面分别插入指定文件中的内容
sed '[<寻址>]r <文件名>'
示例
sed '$r afile' bfile > cfile # 将 afile 内容追加到 bfile 结尾并合并成新的文件 cfile
常用于合并多个文件
w 写文件 ?
?
sed '[<寻址>]w <文件名>'
p 打印
与 替换命令 s 的标志位 /p
不一样.
输出匹配的行(不禁止原始的输出)
sed [选项] '[<寻址>]p'
示例
sed -n '<寻址>p' # 只打印匹配的行
n 提前读入下一行, 并指向下一行
sed 'n'
示例
cat <<EOF | sed 'n'
1
2
3
4
5
EOF
# 输出(由于未作任何操作, 因此原样输出)
1
2
3
4
5
# ----------------------
cat <<EOF | sed -n 'n;p'
1
2
3
4
5
EOF
# 输出
2
4
正常模式
使用n命令后
使用n命令后
图来源: https://blog.51cto.com/studyi...
q 退出命令
遇到匹配项, 在处理完该项后退出
sed '[<寻址>]q'
示例
sed '2q' # 在打印完第二行后退出
sed '/root/q' # 在匹配到某一行有 root 后退出
打印前N行的一个比较
sed 10q filename
读取前10行就退出sed -n '1,10p' filename
逐行读入全部, 只显示1-10行, 更耗时.
= 打印行号
打印出当前行号(单行显示)
不影响正常的打印
sed '='
示例
echo 'a' | sed '=' # 打印结果, 第一行 "1", 第二行 "a"
多行模式空间 pattern space
多行模式空间改变了 sed 的基本流程, 通过使用 N, P, D 读入并处理多行.
主要应对配置文件是多行的情况, 比如 json 或 xml.
综合示例
a.txt 内容如下
1
2
3
4
5
6
7
8
9
# ------------------- 示例1 ---------------------#
sed 'N;N;s/\n/\t/g;' a.txt
1 2 3
4 5 6
7 8 9
# ---------------------示例2 打印梯形结构 -------------------#
# 关键在于利用 D 改变控制流
sed -n 'P;N;s/\n/\t/;s/^/\n/;D' a.txt
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
1 2 3 4 5 6
1 2 3 4 5 6 7
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8 9
b.txt 内容如下
hell
o bash hel
lo bash
# -------------------- 示例 hello bash 替换成 hello sed --------------#
sed 's/^\s*//;N;s/\n//;s/hello bash/hello sed\n/;P;D;' b.txt
hello sed
hello sed
N 将下一行加入到模式空间
读取时, 将下一行加入到模式空间
两行视为同一行, 但中间有个换行符 \n
此时多行模式 . 可以匹配到除了末尾的其他换行符
sed 'N'
示例
sed = <文件> | sed 'N;s/\n/\t/' # 在每行前面加入行号
使用N命令后
图来源: https://blog.51cto.com/studyi...
D 删除模式空间中的第一个字符到第一个换行符
注意
会改变控制流, 不再执行紧随的后续命令, 会再次回到脚本的初始处(但不清空模式空间)
sed 'D'
P 打印模式空间中的第一个字符到第一个换行符
注意
仅仅是打印, 并不会删除打印的部分.
sed 'P'
保持空间 hold space
注意:
保持空间在不存储东西时, 它里面的默认内容是
\n
因此第一次一般是用
h
覆盖掉保持空间的\n
- 保持空间的内容只能临时存储和取出, 无法直接修改
综合示例
# 下述多个都实现了 tac 倒序显示的效果
# 思路: 每次将本轮正确的结果保存在保持空间
cat -n /etc/passwd | head -n 6 | sed -n '1!G;$!x;$p'
cat -n /etc/passwd | head -n 6 | sed -n '1!G;h;$p'
cat -n /etc/passwd | head -n 6 | sed '1!G;h;$!d'
cat -n /etc/passwd | head -n 6 | sed '1!G;$!x;$!d'
cat -n /etc/passwd | head -n 6 | sed -n '1h;1d;G;h;$p';
cat -n /etc/passwd | head -n 6 | sed -n '1h;1!G;h;$p';
sed '=;6q' /etc/passwd | sed 'N;s/\n/\t/;1!G;h;$!d'
# --------------------- 显示结果 --------------------#
6 sync:x:5:0:sync:/sbin:/bin/sync
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
2 bin:x:1:1:bin:/bin:/sbin/nologin
1 root:x:0:0:root:/root:/bin/bash
h 和 H 将模式空间内容存放到保持空间
- h 是覆盖
- H 是追加
注意: 该操作不会清空模式空间(需要清空模式空间可以用 d
)
g 和 G 将保持空间内容取出到模式空间
- g 是覆盖
- G 是追加
注意: 该操作不会清空保持空间
x 交换模式空间和保持空间内容
awk 行编辑器
awk 是一种解释型编码语言, 常用于处理文本数据.
awk 主要是对 sed 的一个补充, 常用于在sed处理完后对相应结果进行调整并输出.
awk 版本
- awk: 初始版本
- nawk: new awk, 是 awk 的改进增强版
gawk: GNU awk, 所有 GNU/Linux 发行版都包含 gawk, 且完全兼容 awk 与 nawk
实际 centos 用的就是 gawk
参考文档:
- The GNU Awk User's Guide
网上很多教程瞎JB写, 建议以官方文档👆 为准
- 精通awk系列
非常不错的系列教程! :+1:
- https://awk.readthedocs.io/en...
不完整.
awk 和 sed 的区别
- awk 更像是脚本语言
- awk 用于"比较规范"的文本处理, 用于统计数量并调整顺序, 输出指定字段.
- 使用 sed 将不规范的文本处理为"比较规范"的文本
awk 脚本的流程控制
BEGIN{}
输入数据前例程, 可选{}
主输入循环END{}
所有文件读取完成例程
个人理解的执行顺序
- 执行开始块(可选)
BEGIN{}
若存在主体块 {}
或结束块
END{}`, 则打开文件并读入数据如果文件无法打开会在此时报错.
主体块允许存在多个, 比如根据不同的匹配模式, 写多个主体块.
- 若存在
<寻址>
或/pattern/
, 则会依次匹配, 通过则对该记录执行{}
- 读完所有记录后, 执行结束块(可选)
END{}
特殊情况, 脚本只包含 BEGIN{}
时, 在执行 awk 命令后面传入参数(非文件名), 此时不会导致报错, 因为不会执行到步骤2(即尝试打开文件).
如果脚本只包含 BEGIN{命令}
, 好像可以缩写成 awk '命令'
??
语法
语法
awk [选项] 'awk脚本内容' [ -- ] [文件...] # 其中任意一部分都是可选的
awk [选项] -f <awk脚本文件> [ -- ] [文件...] # 不直接在命令行中书写 awk 脚本, 而是从指定文件读取
选项
-f <脚本文件>, --file=<脚本文件> # 从指定脚本文件读取 awk 脚本, 而不是默认地从第一个命令行参数读取.
-F, --field-separator <分隔符> # 字段分隔符可以使用正则表达式, 是空格 " ", 也可以在awk脚本中用 FS="," 来修改这一行为
-v <var>="val", --assign <var>="val" # 直接为awk脚本中的变量赋值, 比如 -v suffix=yjx, 然后代码中直接就存在 suffix 这个变量了, 且值是 yjx
--dump-variables[=<file="awkvars.out">] # 将awk中的所有全局变量及其值导出到指定文件中.
awk脚本的块
开始块
BEGIN {} # 特殊模式: 读取输入数据前
主体块
表达式 {} # 若匹配上才执行后续的 action
# eg.
# `$1 == "top" {}`
# `NR >= 2 {}`
# 'length($0)'
/正则/ {} # 正则匹配, 若匹配上才执行后续的 action
# eg.
# /^menu/ 只处理 menu 开头的记录
# /cost/ 只处理本行内容中包含 "cost" 的记录
!/正则/ {} # 正则不匹配
组合模式 {} # 一个 组合模式 通过与(&&),或(||),非(|),以及括弧来组合多个表达式
{} # 每读取一行数据则执行后续的 action
模式1,模式2 {} # 范围模式(range pattern)匹配从与 模式1 相匹配的行到与 模式2 相匹配的行(包含该行)之间的所有行,对于这些输入行,执行 语句 。
结束块
END {} # 特殊模式: 读取完毕后
主体块的action
print 打印(若未配置则默认是 print)
next 对于本行的处理, 跳过后续步骤表达式
{...} 执行 {} 中的脚本
示例.
awk [选项] 'BEGIN{} [<条件>] {} END{}' 文件... # 其中任意一部分都是可选的
# BEGIN{} 输入数据前例程
# {} 主输入循环, <寻址> 是应用于 {} 的
# END{} 所有文件读取完成例程
awk -f <脚本.awk> # 从文件中加载执行的命令, 注意<寻址>要和 { 写在同一行, 不然好像没生效?
# 示例 xx.awk
# BEGIN {
# ...
# }
# /过滤条件/ {
# ...
# }
# END {
# ...
# }
字段引用
$0 # 表示记录整行
$1 $2 ... $n # 表示第1~第n个字段
NF # 标识 $0 被分割的字段数
$NF # 表示最后一个字段, NF 变量表示字段的数量, 比如当前共5个字段, 则 $NF 等价于 $5.
简单示例
awk -F ',' '{print $1,$2,$3}' filename # 逗号分隔, 打印前3个字段
echo "menuentry 'CentOS Linux (5.5.6) 7 (Core)' --class centos" | awk -F "'" '{print $2}' # 提取出内核版本
修改字段或NF值的联动效应
注意以下几种操作
- 修改
$0
会根据FS
, 重新划分字段并自动赋值给$1
,$2
, ... ,NF
.$0=$0
也会触发重新划分字段的操作. 修改
$1
,$2
, ... , 会根据OFS
重新生成$0
, 但不会重新分割.即使是
$1=$1
也会触发上述操作, 当然是用NF=NF
也是可以的.# 利用该特性重新生成去除行首行尾空格, 压缩中间空格的效果 echo " a b c " | awk '{NF=NF; print $0;}' 输出 a b c
- 赋值给不存在的字段, 会新增字段并按需使用空字符串填充中间的字段,并使用
OFS
重新计算$0 - 增加
NF
值,将使用空字符串新增字段,并使用OFS
重新计算$0
- 减少
NF
值,将丢弃一定数量的尾部字段,并使用OFS重新计算$0
正则字面量
这里有个地方容易被坑到!!!
任何单独出现的 /pattern/
都等价于 $0 ~ /pattern/
, 这个在将正则表达式赋值给变量时特别容易被坑, 举例:
if(/pattern/)
等价于if($0 ~ /pattern/)
a = /pattern/
等价于将$0 ~ /pattern/
的匹配返回值(0或1)赋值给 a/pattern/ ~ $1
等价于$0 ~ /pattern/ ~ $1
,表示用$1
去匹配0或1/pattern/
作为参数传给函数时,传递的是$0~/pattern/
的结果0或1
匹配成功时返回1, 失败返回0.
举例
# 这边直接用于匹配没什么问题
awk 'BEGIN { if ("abc" ~ "^[a-z]+$") { print "match"} }'
#输出
match
awk 'BEGIN { if ("abc" ~ /^[a-z]+$/) { print "match"} }'
#输出
match
# 这边将其赋值给其他变量, 此时其实 regex = $0 ~ /^[a-z]+$/, 也就是值 0
awk 'BEGIN { regex=/^[a-z]+$/; print regex; if ("abc" ~ regex) { print "match"} }'
#输出
0
awk 'BEGIN { regex="^[a-z]+$"; print regex; if ("abc" ~ regex) { print "match"} }'
#输出
^[a-z]+$
match
在 awk 中书写正则可以用 /[0-9]+/
也可以用
/[0-9]+/
匹配方式:"str" ~ /pattern/或"str" !~ /pattern/
匹配结果返回值为0(匹配失败)或1(匹配成功)
任何单独出现的/pattern/都等价于$0 ~ /pattern/
if(/pattern/)等价于if($0 ~ /pattern/)
坑1:a=/pattern/等价于将$0 ~ /pattern/的匹配返回值(0或1)赋值给a
坑2:/pattern/ ~ $1等价于$0 ~ /pattern/ ~ $1,表示用$1去匹配0或1
坑3:/pattern/作为参数传给函数时,传递的是$0~/pat/的结果0或1
坑4.坑5.坑6…
内置变量
awk 中可以看作是在一个独立的系统空间中, 因此也有其特殊的系统变量.
注意:
- 字段的引用不能加
$
, 这点与Shell不一样, 不然就变成获取记录中某个字段的值了.
控制 AWK 工作的预定义变量
FS
(field separator)输入数据的分隔符, 默认值是空格awk -F "," # 等价于 awk 'BEGIN {FS=","}' # 在读入文件之前设置字段分隔符
OFS
(output field separator)输出字段分隔符(默认是空格)awk 'BEGIN {OFS=","}'
FIELDWIDTHS
以指定宽度切割字段而非按照 FSFPAT
以正则匹配, 将匹配到的结果作为字段, 而非按照 FS 或 FIELDWIDTHS 划分. 理解为 re_match, 而不是 re_splitFPAT = "([^,]+)|(\"[^\"]+\")" # 上述 FPAT 用于分割以逗号分隔的 csv 文件, 若使用双引号包裹的则视为是一个字段(忽略其中的逗号). # 比如对于数据: abc,"pqr,mno" # $1 值为 abc # $2 值为 "pqr,mno"
https://stackoverflow.com/que...
RS
(record separator)记录分割符(默认是\n
)该变量通常在 BEGIN 块中修改, 修改该变量可以控制 awk 每次读取的数据的范围(默认是读取一行), 读取到的记录是不包含记录分隔符的.
- RS 设置为单个字符: 直接使用该字符来分割记录
- RS 设置为多个字符: 视为正则(非兼容模式), 使用该正则来分割记录.
awk 'BEGIN {RS=":"}' # 将记录分割符设置为 : , 这样每次遇到 : 时就视为一条记录分别处理.
特殊读取需求
RS=""
: 按段落读取(这个特性有用)RS="\0"
: 一次性读取所有数据, 但有些特殊文件包含了空字符\0
RS="^$"
: 真正的一次性读取所有数据, 因此^$
匹配的是空文件RS="\n+"
: 按行读取, 但忽略空行
ORS
(output row separator) 输出记录分隔符(默认是\n
)awk 'BEGIN {OFS=":"}'
CONVFMT
表示数据转换为字符串的格式, 默认值是%.6g
OFMT
表示数值输出的格式, 默认值是%0.6g
, 标识有效位(整数部分加小数部分)最多为6.IGNORECASE
控制是否对大小写敏感, 当该变量设置时, 忽略大小写. 在分割记录时也受该变量影响.awk 'BEGIN{IGNORECASE=1} /amit/' marks.txt
携带信息的预定义变量
文件与行号
FILENAME
当前被处理的文件名在
BEGIN {}
块中, 该变量是未定义的.NR
(number of rows)记录的行号会一直累加, 就算处理多个文件, 该行号也会一直累加.
FNR
(file number of rows)记录的行号(处理不同文件时会重置)当处理多个文件时, 切换到不同文件则该值会重置掉.
NF
(number of fields)字段数量最后一个字段内容可以用 $NF 取出
ARGIND
用于处理多个文件时, 表示当前正在处理的文件的顺序(从 1 开始)awk 'ARGIND==1 {if(FNR>3)print FNR,$3 } ARGIND==2 {if(FNR>1)print FNR,$2} ARGIND==3 {if(FNR<3)print FNR,$NF}' s.log t.log s.log
RT
(Record Termination) 实际记录分割符当 RS 设置为多个字符(正则)时, 在每条记录被读取并分割后,
RT
变量会被设置为实际用于划分记录的字符.
命令行与环境参数
ARGC
命令行位置参数个数("选项"是不包含在内的)ARGV
命令行位置参数数组ARGV[0]
值是命令名本身, 即awk
ARGV[1]
是传入的第1个参数- 范围:
ARGV[0]
~ARGV[ARGC - 1]
ENVIRON
存放系统环境变量的关联数组awk 'BEGIN{print ENVIRON["USER"]}' # 输出: shell 变量 "USER"
PROCINFO
关联数组, 保存进程相关的信息# 打印 awk 进程的Id awk 'BEGIN { print PROCINFO["pid"] }'
ERRNO
用于存储当 getline 重定向失败或 close 函数调用失败时的失败信息.
表达式
赋值操作符
=
=
的左右是可以有空格的.- 字符串拼接中的空格会被忽略
Eg.
var = "hello" "world"
实际上 var 值是
"helloworld"
, 没有中间的空格 - 若是拼接两个字符串变量, 则使用字符串字面量隔开即可.
s3=s1""s2
++
支持前置递增和后置递增
--
支持前置递减增和后置递减
+=
-=
*=
/=
%=
^=
算数操作符
+
-
*
/
%
^
位操作
AND
按位与操作OR
按位或操作XOR
按位异或操作
关系操作符
<
>
<=
>=
==
注意, 判断两个值是否相等要用
==
, 而不是赋值运算符=
!=
~
字符匹配正则表达式!~
布尔操作符
&&
||
!
使用
!
时注意使用括号将相关表达式括起来, 避免写错.
三元运算符
condition expression ? statement1 : statement2
匹配运算符
除了块的条件匹配外, 还可以用于 if
判断之类的.
~
匹配指定正则表达式的, 用于主体块的条件匹配# 仅处理包含 hello 文本的行 awk '$0 ~ "hello"' xx.txt # 注意这里正则是用 / / 包围起来, 而不是双引号 awk 'BEGIN { if ("[abc]" ~ /\[.*\]/) { print "match";}}' #输出 #match # 注意这里用双引号括起来时, 里面用了双斜杠来处理正则的 [ awk 'BEGIN { if ("[abc]" ~ "\\[abc\\]") { print "match";}}' #输出 #match
!~
不匹配指定正则表达式的, 用于主体块的条件匹配# 仅处理不含 hello 文本的行 awk '$0 !~ "hello"' marks.txt
条件和循环
注意:
表达式结果: 0为false, 1为true
这个与 Shell 是相反的.
- 影响控制的语句:
break
,continue
综合示例
cat kpi.txt
user1 70 72 74 76 74 72
user2 80 82 84 82 80 78
#-------------- 计算每行数值的总之和平均值 -----------#
awk '{total=0; avg=0; for (i=2;i<=NF;i++) {total+=$i;} avg=total/(NF-1); print $1,total,avg;}' kpi.txt
user1 438 73
user2 486 81
if 条件语句
if (表达式) {
} else if (表达式) {
} else {
}
执行多条语句要用 {}
, 只有一条语句时可忽略.
for 循环
for (初始值; 循环判断条件; 累加) {
}
while 循环
while (表达式) {
}
do 循环
do {
} while(表达式)
数组
普通数组
awk 的数组实际上是关联数组, 可通过下标(数字实际上也是字符串)依次访问
比如
arr[1]
和arr["1"]
实际上是对同一个 key 操作支持多维数组
gawk(可以认为是 awk 的加强版, 替代版, 至少 centos 的 awk 实际就是 gawk) 支持真正意义上的多维数组.
awk 还有一种更"原始"的使用一维数组模拟多维数组的, 但在 gawk 中已经没必要了.
# 定义
## 下标可以是数字(视为字符串)或字符串
数组名[下标] = 值
# 遍历
for (变量 in 数组名) {
数组名[变量] # 获取对应数组值
}
# 删除数组
delete 数组
# 删除数组元素
delete 数组[下标]
# 判断数组中是否存在指定"键"
if (key in array)
# 判断数组中是否不存在指定"键", 注意这里额外加了一个括号, 不能省略了
if (!(key in array))
命令行参数数组
ARGC
命令行位置参数个数ARGV
命令行位置参数数组ARGV[0]
值是命令名本身, 即awk
ARGV[1]
是传入的第1个参数- 范围:
ARGV[0]
~ARGV[ARGC - 1]
示例
cat argv.awk
内容
BEGIN {
for (i=0; i<ARGC; i++) {
print ARGV[i];
}
print ARGC;
}
# --------------------------------#
awk -f argv.awk afile 11 22 33
输出
awk # ARGV[0]
afile # ARGV[1]
11 # ARGV[2]
22 # ARGV[3]
33 # ARGV[4]
5 # ARGC
此处不会报错是因为awk脚本中只包含 BEGIN {}
部分, 因此不会将参数视为文件名并尝试打开.
数组函数
length(数组)
获取数组长度asort(数组a[, 数组b, ...])
对数组a的值进行排序,并且会丢掉原先键值(重新生成数字递增的 key 来替代), 并将结果赋予数组 b (若未传, 则直接修改数组 a).arorti(数组a[, 数组b, ...])
对数组a的键进行排序, 并将结果赋予数组 b (若未传, 则直接修改数组 a).
函数
算术函数
sin()
cos()
atan2(y,x)
exp(x)
返回自然数 e 的 x 次方sqrt()
平方根log(x)
计算 x 的自然对数int()
转换为整数(忽略小数部分)rand()
伪随机数, 范围[0,1)
, 默认使用srand(1)
初始化随机种子.若不使用
srand()
会发现每次获取的所谓随机数都是一样的.srand(); print rand();
srand([seed])
重置随机种子, 默认种子采用当前时间的 epoch 值(秒级别)
位操作函数
compl(num)
` 按位求补lshift(num, offset)
左移N位rshift(num, offset)
右移N位
字符串函数
awk 中涉及字符索引的函数, 索引位都是从 1 开始.
注意, 不同 awk 版本, 函数参数个数是有可能不一样的.
sprintf(format, expr1, ...)
返回格式化后的字符串示例:
a = sprintf("%10s\n", "abc")
length(s)
返回字符串/数组的长度strtonum(str)
将字符串转换为十进制数值如果 str 以0开头,则将其识别为8进制
如果 str 以0x或0X开头,则将其识别为16进制
tolower(str)
转换为小写toupper(str)
转换为大写查找
index(str,substr)
在目标字符串中查找子串的位置, 若返回 0 则表示不存在.match(string, regexp, array)
字符串正则匹配, 将匹配结果保存在 arr 数组中.变量
RLENGTH
表示 match 函数匹配的字符串长度.变量
RSTART
表示 match 函数匹配的字符串的第一个字符的位置.awk 'BEGIN { if (match("One Two Three", "re")) { print RLENGTH } }' # 输出 2 awk 'BEGIN { if (match("One Two Three", "Thre")) { print RSTART } }' # 输出 9
cat test # this is wang,not wan # that is chen,not che # this is chen,and wang,not wan che awk '{match($0, /.+is([^,]+).+not(.+)/, a); print a[1],a[2]}' test # wang wan # chen che # chen wan che
替换
gsub(regx,sub [,targe=$0])
全局替换, 会直接修改原始字符串, 返回替换成功的次数.如果
target
使用$0
,$...
等, 那么替换成功后会使用 OFS 重新计算$0
这边
sub
不支持反向引用, 只能使用&
来引用匹配成功的部分sub(regx,sub [,targe=$0])
只替换第一个匹配的, 会直接修改原始字符串, 返回替换成功的次数.gensub(regx, sub [, how [, target]])
不修改原字符串, 而是返回替换后的字符串. 可以完全替代gsub
和sub
how
: 指定替换第几个匹配, 比如 1 表示只替换第一个匹配,g
或G
表示全局替换这是 gawk 提供的函数, 其中
sub
支持使用\N
引用分组匹配, 或&
,\0
来标识匹配的整个结果.awk 'BEGIN { a = "111 222" b = gensub(/(.+) (.+)/, "\\2 \\1, \\0, &", "g", a) print b }' # 输出 222 111, 111 222, 111 222
截取
substr(str,pos,num=剩余所有)
从指定位置开始截取一个子串
分割
split(str, arr [, 正则分隔符=FS])
字符串分割为数组, 并将其保存到第2个参数中, 函数返回值是分割的数patsplit(str, arr[, 正则分隔符=FPAT])
使用正则捕获匹配的字符串, 并将其保存到第2个参数中.
若不清楚的, 可以在 man awk
中搜索相应关键字
时间函数
systime
返回当前时间戳(秒级)mktime("YYYY MM DD HH mm SS [DST]")
根据给定的字符串格式, 返回其对应的时间戳# 格式: 年 月 日 时 分 秒 awk 'BEGIN{print mktime("2020 10 10 17 51 59")}'
strftime([format [, timestamp[, utc-flag]]])
将时间戳(默认是当前时间)转换为字符串表示awk 'BEGIN {print strftime("Time = %Y-%m-%d %H:%M:%S")}' 输出 Time = 2020-10-12 17:53:37
SN 描述 %a 星期缩写(Mon-Sun)。 %A 星期全称(Monday-Sunday)。 %b 月份缩写(Jan)。 %B 月份全称(January)。 %c 本地日期与时间。 %C 年份中的世纪部分,其值为年份整除100。 %d 十进制日期(01-31) %D 等价于 %m/%d/%y. %e 日期,如果只有一位数字则用空格补齐 %F 等价于 %Y-%m-%d,这也是 ISO 8601 标准日期格式。 %g ISO8610 标准周所在的年份模除 100(00-99)。比如,1993 年 1 月 1 日属于 1992 年的第 53 周。所以,虽然它是 1993 年第 1 天,但是其 ISO8601 标准周所在年份却是 1992。同样,尽管 1973 年 12 月 31 日属于 1973 年但是它却属于 1994 年的第一周。所以 1973 年 12 月 31 日的 ISO8610 标准周所在的年是 1974 而不是 1973。 %G ISO 标准周所在年份的全称。 %h 等价于 %b. %H 用十进制表示的 24 小时格式的小时(00-23) %I 用十进制表示的 12 小时格式的小时(00-12) %j 一年中的第几天(001-366) %m 月份(01-12) %M 分钟数(00-59) %n 换行符 (ASCII LF) %p 十二进制表示法(AM/PM) %r 十二进制表示法的时间(等价于 %I:%M:%S %p)。 %R 等价于 %H:%M。 %S 时间的秒数值(00-60) %t 制表符 (tab) %T 等价于 %H:%M:%S。 %u 以数字表示的星期(1-7),1 表示星期一。 %U 一年中的第几个星期(第一个星期天作为第一周的开始),00-53 %V 一年中的第几个星期(第一个星期一作为第一周的开始),01-53。 %w 以数字表示的星期(0-6),0表示星期日 。 %W 十进制表示的一年中的第几个星期(第一个星期一作为第一周的开始),00-53。 %x 本地日期表示 %X 本地时间表示 %y 年份模除 100。 %Y 十进制表示的完整年份。 %z 时区,表示格式为+HHMM(例如,格式要求生成的 RFC 822或者 RFC 1036 时间头) %Z 时区名称或缩写,如果时区待定则无输出。
其他函数
getline
请参照下方的 "getline" 部分.
close(xxx [, from|to])
关闭文件、shell进程, 可以仅关闭某一端.
close(xxx, "to")
表示关闭该管道的写入端,close(xxx, "from")
表示关闭该管道的输出端.注意!!!! awk 中任何文件都只会在第一次使用时打开, 之后都不会再重新打开(而是从上次的读取位置继续). 因此只有在关闭之后, 再次使用时才会重新打开.
next
跳过对当前记录的后续处理.会回到 awk 循环的头部, 读取下一行.
nextfile
停止处理当前文件, 从下一个文件开始处理.return xx
函数返回值system("shell命令")
执行 shell 命令, 并返回退出的状态值, 0表示成功.flush([output-expr])
刷新打开文件或管道的缓冲区如果没有提供 output-expr,fflush 将刷新标准输出。若 output-epxr 是空字符串 (""),fflush 将刷新所有打开的文件和管道。
close(expr)
关闭文件句柄 ???awk 'BEGIN { cmd = "tr [a-z] [A-Z]" print "hello, world !!!" |& cmd # "&|" 表示双向管道通信 close(cmd, "to") # 关闭其中一个方向的管道, 另一个是 "from" 方向 cmd |& getline out # 使用 getline 函数将输出存储到 out 变量中 print out; close(cmd); # 关闭管道 }' 输出 HELLO, WORLD !!!
exit <code=0>
终止脚本
自定义函数
function 函数名(参数) {
awk 语句
return awk变量
}
注意
- 自定义函数的书写不能再
BEGIN{}
,{}
,END{}
的里层
getline
getline 函数用于读取一行数据.
根据不同情况, 返回值不一样.
- 若读取到数据, 返回 1.
- 若遇到 EOF, 返回 0.
- 发生错误, 返回负数. 如-1表示文件无法打开,-2表示IO操作需要重试(retry)。在遇到错误的同时,还会设置 ERRNO 变量来描述错误.
awk '{print $0; getline; print $0}' marks.txt
# 建议使用 getline 时判断一下是否读取成功
awk 'BEGIN {getline; if ((getline) > 0) {...}}'
从当前文件读取
- 使用
getline
不带参数时, 表示从当前正在处理的文件中立即读取下一条记录并保存到$0
, 同时进行字段分割(分别存到$1
,$2
,...), 同时会设置 NF, RT, NR, FNR, 然后继续执行后续代码. - 执行
getline <变量名>
时, 会将读取结果保存到对应变量中, 而不会更新$0
, 也不会更新 NF,$1
,$2
, ..., 此时仅仅会更新 RT, NR, FNR.
从其他文件读取
执行
getline < "filename"
表示从指定文件读取一条记录并保存到$0
中(同时进行字段分割), 及 NF. 至于 NR, FNR 则不会更新.每次读取记录后会自动记录读取的位置.
配合
while
及getline
的返回值可以遍历完文件.注意
getline < abc
与getline < "abc"
是两码事, 一个是从 abc 变量指向的文件读取, 一个是读取 abc 文件- 执行
getline 变量名 < "filename"
表示从指定文件读取一条记录并保存到指定变量中.
从 shell 命令输出结果中读取
cmd | getline
:从Shell命令cmd
的输出结果中读取一条记录保存到$0
中会进行字段划分,设置变量
$0
NF
$N
RT
,不会修改变量NR
FNR
cmd | getline var
:从Shell命令cmd
的输出结果中读取数据保存到var
中除了
var
和RT
,其它变量都不会设置
如果要再次执行 cmd 并读取其输出数据,则需要close关闭该命令, 示例:。
# 若屏蔽下方的 close 函数, 则再次读取该 cmd 时为空. 因此需要关闭先.
awk 'BEGIN {cmd = "seq 1 5"; \
while((cmd | getline) > 0) {print}; \
close(cmd); \
while((cmd | getline) > 0) {print}; \
}'
可以方便地使用 shell 给 awk 中变量赋值
awk 'BEGIN {get_date = "date +\"%F %T\""; \
get_date | getline cur_date; \
print cur_date; \
close(get_date);
}'
输出
2020-10-13 19:14:17
将数据传给 shell 处理完(coprocess), 再读取
这里要利用 coprocess, 也就是 |&
操作符.
awk 可以利用 |&
将一些不好处理的数据传给 shell 来处理后, 再从 shell 中读取结果, 继续在 awk 中处理.
使用 shell 的排序功能
# sort 命令会等待数据写入完毕后(即 EOF 标记)才开始排序, 因此实际是在执行 close(CMD, "to") 时才开始执行.
awk 'BEGIN {
CMD = "sort -k2n";
print "6 66" |& CMD;
print "3 33" |& CMD;
print "7 77" |& CMD;
close(CMD, "to");
while ((CMD |& getline) > 0) {
print;
}
close(CMD);
}'
输出:
3 33
6 66
7 77
使用注意:
awk-print |& cmd
会直接将数据写进管道, cmd 可以从管道中获取数据- 强烈建议在awk_print写完数据之后加上
close(cmd,"to")
,这样表示向管道中写入一个EOF标记,避免某些要求读完所有数据再执行的cmd命令被永久阻塞.关闭管道另一端可以使用
close(cmd, "from")
- 如果
cmd
是按块缓冲的,则getline
可能会陷入阻塞。这时可将cmd
部分改写成stdbuf -oL cmd
以强制其按行缓冲输出数据CMD="stdbuf -oL cmdline";awk_print |& CMD;close(CMD,"to");CMD |& getline
高级输出
print 输出重定向
print "..."
print "..." > 文件名
重定向输出print "..." >> 文件名
重定向输出这玩意比使用
system
函数再调用 echo 快了好几个量级print "..." | "shell 命令"
awk 将创建管道, 并启动 shell 命令. print 产生的数据放入管道, shell 命令则从管道中读取数据.print "..." |& "shell 命令"; "shell 命令" | getline
和上面的|
不同之处在于, 这里是将数据交给 Coprocess, 之后 awk 还需要再从 Coprocess 取回数据.Coprocess 执行 shell 命令的时候, 结果是不输出到标准输出的, 而是需要从管道中自行读取.
支持重定向到
- 标准输入
/dev/stdin
- 标准输出
/dev/stdout
- 标准错误
/dev/stderr
若 print 输出后发现后续的管道命令没有内容, 那其实是因为 awk 的输出存在缓存, 可使用 fflush()
函数刷新缓冲区.
关于 |
和 |&
使用上区别的示例
# 这里用的是 |, 执行结果直接输出到标准输出
awk 'BEGIN {
cmd = "tr \"[a-z]\" \"[A-Z]\""
print "hello" | cmd
> }'
# 这里用的是 |&, 执行结果需要手动从 Coprocess 管道读取
awk 'BEGIN {
cmd = "tr \"[a-z]\" \"[A-Z]\""
print "hello" |& cmd;
close(cmd, "to");
cmd |& getline line;
print line;
close(cmd);
}'
输出
HELLO
printf 格式化
printf(format, value1, value2, ...)
参数
format 格式
%c (将ASCII转换为)字符
%s 字符串
%d,%i 整数
%e,$e 科学计数法表示
%f,%F 浮点数
%g,%G 浮点数, 会移除对数值无影响的 0
%o 无符号八进制
%u 无符号十进制
%x,%X 无符号十六进制
%% 百分号
注意: 输出时不会自动换行
示例
printf("total pay for %s is $%.2f\n", $1, $2 * $3)
# %-8s 字符串, 8个字符宽度, 左对齐
# %6.2f 浮点数, 6个字符宽度, 保留2位小数
printf("%-8s %6.2f\n", $1, $2 * $3)
参考: https://awk.readthedocs.io/en...若仅仅是为了格式化字符串(不输出), 可以用 sprintf 函数.
format 支持的转义序列
- 换行符
\n
- 水平制表符
\t
垂直制表符
\v
理解为输出光标垂直向下移动一行
退格符
\b
理解为输出光标后退一格.
回车符
\r
理解为光标会回退到当前行的开头(常用于覆盖本行)
换页符
\f
这个效果...得试一下才行
format 的 %
的可选参数
宽度
只有当字段的宽度比要求宽度小时该标示才会有效, 默认使用空格字符填充.
printf("%10d", 1) 输出 1
前导零
0
只有当字段的宽度比要求宽度小时该标示才会有效
awk 'BEGIN {printf("%010d", 1)}' 输出 0000000001
左对齐
-
在
%
和数字之间使用-
符号即可指定左对齐awk 'BEGIN {printf("%-10d", 1)}' 输出 1
符号前缀
+
使用
+
, 在输出数字时, 将其正负号也输出.awk 'BEGIN {printf("%+10d", 1)}' 输出 +1
保留进制标识
#
awk 'BEGIN {printf("%#o %#X\n", 10, 10)}' 输出 012 0XA
Examples
打印出当前的可用内核列表
并显示序号
awk -F "'" '/^menuentry/ {print x++,$2}' /boot/grub2/grub.cfg
输出
0 CentOS Linux (5.5.6) 7 (Core)
1 CentOS Linux (3.10.0-1062.12.1.el7.x86_64) 7 (Core)
2 CentOS Linux (3.10.0-957.el7.x86_64) 7 (Core)
3 CentOS Linux (0-rescue-d64aa77b8f014365aa6557986697df9c) 7 (Core)
统计当前tcp的各个状态及数量
netstat -ntp | awk '/^tcp / {S[$6]++} END{for (i in S) print i,S[i];}'
输出
TIME_WAIT 108
ESTABLISHED 154
统计每个接口的访问时间
time cat 20_10_*.txt | grep "request cost" | gawk -v suffix=_test -f ../stat_method.awk
stat_method.awk
BEGIN {
DEBUG = 1
methodRecordFile = "method_record"suffix".csv"
methodStatsFile = "method_stats"suffix".csv"
if (DEBUG) {
methodRecordFile = "/dev/stdout"
methodStatsFile = "/dev/stdout"
}
print methodRecordFile
print methodStatsFile
}
{
method=substr($10,2,length($10)-2)
method=substr(method,1,length(method)-7)
timeStr=$1" "$2
timeMs=$6 + 0.01
uid=substr($11,2,length($11)-2)
msLevel=(int(timeMs/100) + 1) * 100
T[method][msLevel]++
if (!(method in Stats)) {
Stats[method]["name"] = method
Stats[method]["max"] = 0
Stats[method]["mean"] = 0
Stats[method]["count"] = 0
Stats[method]["sum"] = 0
Stats[method]["sumX2"] = 0
Stats[method]["min"] = 9999999999999
}
if (timeMs > Stats[method]["max"]) {
Stats[method]["max"] = timeMs
}
if (timeMs < Stats[method]["min"]) {
Stats[method]["min"] = timeMs
}
Stats[method]["sumX2"] += timeMs * timeMs
Stats[method]["count"]++
Stats[method]["sum"] += timeMs
if (NR % 10000 == 0) {
print "已处理 "NR" 条记录"
}
}
END {
print "----------- 总共处理 "NR" 条记录 -----------"
print "method,msLevel,count" > methodRecordFile
# print "method,msLevel,count"
for (m in T) {
for (l in T[m]) {
print m","l","T[m][l] >> methodRecordFile
# print m","l","T[m][l]
}
}
print "----------- done -----------"
print "method,min,max,mean,sd(标准差),count,sum(秒)" > methodStatsFile
printf("%-30s\tmin\tmax\tcount\tmean\tsd(标准差)\t,sum(秒)\n", "method")
for (m in Stats) {
Stats[m]["mean"] = Stats[m]["sum"] / Stats[m]["count"]
Stats[m]["sd"] = sqrt(Stats[m]["sumX2"] / Stats[m]["count"] - Stats[m]["mean"] * Stats[m]["mean"])
# for (n in Stats[m]) {
# print "Stats["m"]["n"]= " Stats[m][n]
# }
print m "," Stats[m]["min"] "," Stats[m]["max"] "," Stats[m]["mean"] "," Stats[m]["sd"]"," Stats[m]["count"]","Stats[m]["sum"]/1000 >> methodStatsFile
printf("%-30s\t%.2f\t%.2f\t%.0f\t%.2f\t%d\t%.2f\n", substr(m, 0, 30), Stats[m]["min"], Stats[m]["max"],Stats[m]["mean"],Stats[m]["sd"], Stats[m]["count"], Stats[m]["sum"]/1000)
}
print "----------- done -----------"
print methodRecordFile
print methodStatsFile
}
格式化空白
cat > a.txt <<'EOF'
aaaa bbb ccc
bbb aaa ccc
ddd fff eee gg hh ii jj
EOF
awk 'BEGIN{OFS=" "} {NF=NF; print}' a.txt
输出
aaaa bbb ccc
bbb aaa ccc
ddd fff eee gg hh ii jj
打印 ini 文件中的某一段
awk -v scope="extras" 'BEGIN {scope="["scope"]"}
$0 == scope {
print;
while ((getline) > 0) {
if ($0 !~ /\[.*\]/) { print $0; } else { exit }
}
}
' /etc/yum.repos.d/CentOS-Base.repo
输出如下 👇
[extras]
name=CentOS-$releasever - Extras
mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=extras&infra=$infra
#baseurl=http://mirror.centos.org/centos/$releasever/extras/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
#additional packages that extend functionality of existing packages
处理字段中包含字段分割符情况(csv)
echo 'Robbins,Arnold,"1234 A Pretty Street, NE","MyTown",MyState,12345-6789,USA' | awk 'BEGIN { FPAT="[^,]+|\"[^\"]+\"" } { print NF" "$3}'
输出如下 👇
7 "1234 A Pretty Street, NE"
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。