关于linux:⭐Linux实战技能100讲个人笔记-5-文本操作篇

31次阅读

共计 22619 个字符,预计需要花费 57 分钟才能阅读完成。

[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 的模式空间, 其根本工作形式

  1. 将文件以行为单位读取到内存(称作模式空间)
  2. 应用 sed 的每个脚本顺次对该行进行操作
  3. 打印模式空间的内容并清空
  4. 读取下一行, 反复执行上述步骤

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 脚本的流程管制

  1. BEGIN{} 输出数据前例程, 可选
  2. {} 主输出循环
  3. END{} 所有文件读取实现例程

集体了解的执行程序

  1. 执行开始块(可选) BEGIN{}
  2. 若存在主体块 {} 或完结块 END{}`, 则关上文件并读入数据

    如果文件无奈关上会在此时报错.

    主体块容许存在多个, 比方依据不同的匹配模式, 写多个主体块.

  3. 若存在< 寻址 >/pattern/, 则会顺次匹配, 通过则对该记录执行 {}
  4. 读完所有记录后, 执行完结块(可选) 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 以指定宽度切割字段而非依照 FS
  • FPAT 以正则匹配, 将匹配到的后果作为字段, 而非依照 FS 或 FIELDWIDTHS 划分. 了解为 re_match, 而不是 re_split

    FPAT = "([^,]+)|(\"[^\"]+\")"
    
    # 上述 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]]) 不批改原字符串, 而是返回替换后的字符串. 能够齐全代替 gsubsub

        how: 指定替换第几个匹配, 比方 1 示意只替换第一个匹配, gG 示意全局替换

        这是 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 则不会更新.

      每次读取记录后会自动记录读取的地位.

      配合 whilegetline 的返回值能够遍历完文件.

      留神 getline < abcgetline < "abc" 是两码事, 一个是从 abc 变量指向的文件读取, 一个是读取 abc 文件

    • 执行 getline 变量名 < "filename" 示意从指定文件读取一条记录并保留到指定变量中.

    从 shell 命令输入后果中读取

    • cmd | getline:从 Shell 命令 cmd 的输入后果中读取一条记录保留到 $0

      会进行字段划分,设置变量 $0 NF $N RT,不会批改变量 NR FNR

    • cmd | getline var: 从 Shell 命令 cmd 的输入后果中读取数据保留到 var

      除了 varRT,其它变量都不会设置

    如果要再次执行 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"

    正文完
     0