Skip to content

Linux-awk指令

1. 使用场景

1.1 截断history输出

  • 比如在bash编程当中,需要取一些history出来
  • 但是history command的形式如下:1 xxxx_command,需要截断前面的数字
shell
local selected_command=$(history | fzf | awk '{$1=""; print $0}' | tr -d '\n')
  • 怎么理解上面的命令
    • awk本质上相当于awk语言的解释器
    • 执行'{$1=""; print $0}'
      • $1:第一列设置为空,等于去掉数字索引字符
      • $0:打印一整行
    • tr:相当于 transliterate(转写) 的缩写,目的在于去掉换行符

1.2 检索文件特定字段

  • 比如需要检测某个文件,是否有形如url:xxxxxx的行,可以如下操作
shell
url=$(awk '/url:/ {print $2}' ${absolute_file_path})
  • /url: /: 匹配包含url: 的行
  • 该行的格式默认其实是url: xxxxxxxx,那么$2对应的其实就是xxxxxx(相当于第二列)

  • 再比如,需要匹配以#开头的行
shell
title=$(awk '/^# / {print $0; exit}' ${absolute_file_path})
  • 这里的exit说明匹配到第一次就exit掉

  • 再比如,需要匹配只包含---行(匹配出现的第二次,因为需要获取到yaml结束行号)
shell
# NR 是行号
awk_script='
/^---$/ {
  count++;
  if (count == 2) {
    print NR;
    exit;
  }
}'

end_yaml_line=$(awk "${awk_script}" "${file_name}")

1.3 结合目录

sh
ls | awk '{print $0}'
  • 打印出来的结果类似于 ls -l,多行输出
  • 因为 ls 默认的 IFS/n ,因此可以被 awk 输出

2. 基本功能

INFO

Proto Recv-Q Send-Q Local-Address Foreign-Address State tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN tcp 0 0 coolshell.cn:80 124.205.5.146:18245 TIME_WAIT tcp 0 0 coolshell.cn:80 61.140.101.185:37538 FIN_WAIT2 tcp 0 0 coolshell.cn:80 110.194.134.189:1032 ESTABLISHED tcp 0 0 coolshell.cn:80 123.169.124.111:49809 ESTABLISHED tcp 0 0 coolshell.cn:80 116.234.127.77:11502 FIN_WAIT2 tcp 0 0 coolshell.cn:80 123.169.124.111:49829 ESTABLISHED tcp 0 0 coolshell.cn:80 183.60.215.36:36970 TIME_WAIT tcp 0 4166 coolshell.cn:80 61.148.242.38:30901 ESTABLISHED tcp 0 1 coolshell.cn:80 124.152.181.209:26825 FIN_WAIT1 tcp 0 0 coolshell.cn:80 110.194.134.189:4796 ESTABLISHED tcp 0 0 coolshell.cn:80 183.60.212.163:51082 TIME_WAIT tcp 0 1 coolshell.cn:80 208.115.113.92:50601 LAST_ACK tcp 0 0 coolshell.cn:80 123.169.124.111:49840 ESTABLISHED tcp 0 0 coolshell.cn:80 117.136.20.85:50025 FIN_WAIT2 tcp 0 0 :::22 :::* LISTEN

2.1 内置符号

$0当前记录(这个变量中存放着整个行的内容)
$1~$n当前记录的第n个字段,字段间由FS分隔
FS输入字段分隔符 默认是空格或Tab
NF当前记录中的字段个数,就是有多少列
NR已经读出的记录数,就是行号,从1开始,如果有多个文件话,这个值也是不断累加中。
FNR当前记录数,与NR不同的是,这个值会是各个文件自己的行号
RS输入的记录分隔符, 默认为换行符
OFS输出字段分隔符, 默认也是空格
ORS输出的记录分隔符,默认为换行符
FILENAME当前输入文件的名字
  • awk 当中,Fields 被定义为Records 被定义为
  • NF: Number of Fields 列的总量
  • NR: Number of Records 行的总量
  • FSFields Separator 使用什么符号来区分列
  • RS: Records Separator 使用什么符号来区分行
  • OFS: Outpt Fields Separator 使用什么符号在输出的时候作为行的分隔符(你希望输出是什么样子的),必须显式 printOFS 的设置才生效
  • 指定多个分隔符:awk -F '[;:]'
  • demo 如下
sh
awk -F: '{print $1,$3,$6}' ORS="\t end \n" OFS="\t****\t" /etc/passwd
  • 如果需要打印最后一列(这和 ls -l 命令的配合是非常常见的
sh
ls -l | awk '{print $NF}' | tail -n +2

2.2 正则模式匹配

  • 匹配方式一定程度上类似 grep,例子如下
sh
awk 'NR == 1 || /LISTEN/' netstat.txt
  • 实际上,上面的例子省略了一些信息,其等价于
sh
awk 'NR == 1 || $0 ~ /LISTEN/' netstat.txt
  • 等价于,给一整行添加规则为包含 ’Listen‘ 的正则匹配

  • 如果想要给第 6 列添加正则规则,例子如下
sh
awk 'NR == 1 || $6  ~ /LISTEN|ESTAB|FIN/ {print $6}' OFS="  ==== " netstat.txt

  • 如果需要反向匹配,请使用 !~ ,例子如下
sh
awk '$6 !~ /WAIT/ || NR == 1 {print NR,$4,$5,$6}' ORS="    end \n" OFS="    |    "   netstat.txt

  • ls / grep / xargs 等命令可以自由配合
  • 比如以下场景,我的目录打印出来的内容如下 image.png
  • 我只想保留 1.split_by_filed.shnetstat.txt 这两个文件,其他文件都想删除掉
  • 执行命令如下
sh
ll | awk '{print $NF}' | tail -n +2 \
| grep -vE '1\..*|.*\.txt' \
| xargs -I {} rm -r {}

2.3 拆分文件

  • 安装第6列的类别,将其分类成数个不同的文件,命令如下
sh
# 不拆分表头
awk 'NR!=1{print > "./split_1/"$6}' netstat.txt
  • $6 有如下几种可能的分类 ESTABLISHED FIN_WAIT1 FIN_WAIT2 LAST_ACK LISTEN TIME_WAIT
  • 注意,$6 需要写在双引号外面,写在里面就会被当成普通字符串
  • 执行结果如下所示 image.png

2.4 统计频次

sh
awk 'NR!=1{a[$6]++;} END {for (i in a) print i ", " a[i];}' netstat.txt
  • 一般像这种建议直接写成多行脚本,将 awk 脚本的内容提取成单独的变量

2.5 字符串拼接

  • 将多个列添加到 content 当中去。
awk
    for(i=3; i<=NF; i++) {
        content=content$i
    }