概述

  • 参考文档:

  • 这批命令的共同点:用过不等于会用,各有自己的执行模型,值得专门学一遍

  • 三个模型一句话:

    • sed:逐行流编辑,模式空间里改完就走
    • awk:记录与字段模型,天然是门小语言
    • coreutils 组合子:sort/uniq/xargs等在管道里各司其职
  • 一条典型流水线:

    flowchart LR
      A["输入"] --> B["sed 改"]
      B --> C["awk 算"]
      C --> D["sort 排"]
      D --> E["uniq 聚"]
      E --> F["xargs 执行"]

快速开始

console
$ sed 's/foo/bar/g' file > out         # 全局替换
$ awk '{sum += $1} END {print sum}' data.txt    # 第一列求和
$ cat words | sort | uniq -c | sort -rn | head  # 词频统计

sed

  • 工作模型:逐行读入模式空间,执行完脚本后输出,默认每行都回显,-n关掉回显后用p精确控制输出
  • 替换s/正则/替换/标志:
    • 默认只换每行第一处,g全局,2g从第2处起,i忽略大小写
    • &指代整个匹配,\1到\9指代分组,默认 BRE 里分组写\(...\),-E切到 ERE 后直接(...)
    • 分隔符可换:s|/usr|/opt|替换路径时免转义
  • 地址限定:
    • 5s/a/b/只改第5行,$末行,/start/,/end/s/…区间,!取反
    • 多条命令用分号串联:sed '/^#/d; /^$/d' config删注释与空行
  • 常用命令:d删除行、p打印、a text行后追加、i text行前插入、c text整行替换、y/abc/xyz/字符映射
  • 高级特性:N把下一行并进模式空间做多行处理、h/G借助保持空间暂存、:label与b/t做循环,简单场景够用时不要上这些
  • 原地修改:-i直接写回文件,实现是重写整个文件,先裸跑确认输出,重要文件用-i.bak留备份

awk

  • 模型:按记录处理,默认一行一记录,空白切字段:

    • $0整行,$1到$n各字段,NF当前行字段数,NR全局行号,FNR当前文件内行号
  • 程序结构是模式 { 动作 },模式命中才执行动作:

    • BEGIN {}处理输入前执行,放初始化与表头
    • END {}全部处理完执行,放汇总统计
    • 只有动作没有模式等于每行都执行
    console
    $ awk -F: '{print $1, $7}' /etc/passwd            # 冒号分隔取两列
    $ awk '$3 > 100 {print $1}' data.txt              # 条件过滤
    $ awk '{s += $2} END {print s, s/NR}' data.txt    # 求和与均值
    
  • 变量与数组:变量无需声明,数组是关联数组,下标可以是任意字符串:

    console
    $ awk '{c[$1]++} END {for (k in c) print k, c[k]}' access.log   # 按键计数
    
  • 内置函数:length、substr、split、index、sub/gsub替换、printf格式化输出、sprintf返回字符串

  • 双文件操作:NR==FNR只在第一个文件里成立,配合next做交集与补集:

    console
    $ awk 'NR==FNR {a[$1]; next} $1 in a' f1 f2       # 输出两文件第一列的交集
    
  • 命令行传参:-v var=value在BEGIN里可用,-F,设输入分隔,-v OFS='|'设输出分隔:

    console
    $ awk -F, -v OFS='|' '{$1 = $1; print}' data.csv   # 重建记录让 OFS 生效
    
  • 实现:gawk功能最全,mawk更快但方言有差异,可移植脚本头部写#!/usr/bin/awk -f

wc 与计数陷阱

  • wc默认输出行数、词数、字节数,单独指定:-l行、-w词、-c字节、-m字符(受 locale 影响)、-L最长行宽
  • -l数的是换行符个数,末行没有换行符时那一行不计入,printf 'a' | wc -l结果是0,统计日志先确认末行有换行

列与字符(cut、tr、paste、comm)

  • cut按列切:

    • -d, -f 1,3按分隔符取第1、3列,-f 2-取到行尾
    • -c1-8按字符位置切
    • 分隔符只能是单个字符,多字符分隔的场景换awk -F
  • tr做字符级变换,不是正则:

    • tr a-z A-Z转大写,tr -d '\r'删字符,tr -s ' '压缩连续重复
    • 只吃 stdin,文件要用< file喂进去
  • paste横向合并:两文件按行拼接,-d改分隔符,paste -sd, file把多行并成一行

  • comm比较两个已排序文件,输出三列:仅前者有、仅后者有、共有,-1 -2 -3关掉对应列:

    console
    $ comm -12 <(sort a.txt) <(sort b.txt)    # 求交集
    

聚合(sort、uniq)

  • sort是文本流水线的枢纽:

    • -n按数值、-h按人类单位(2K、1M)、-r降序、-u去重、-R随机、-f忽略大小写
    • -t, -k2,2n按第2列数值排序,-k要写死终点start,end,只写-k2会比较从第2列到行尾的整段
    • -s稳定排序,-S 2G加大内存缓冲
  • uniq只比较相邻行,所以几乎总跟在sort后面:

    • -c带计数、-d只留重复行、-u只留唯一行
  • 经典组合,词频 Top 10:

    console
    $ tr -cs 'a-zA-Z' '\n' < text | sort | uniq -c | sort -rn | head
    

执行与管道(xargs、tee)

  • xargs把 stdin 转成命令参数,是"管道流"与"命令行参数"之间的桥:
    • -n 1每次只传一个参数,-I{}自定义占位符:... | xargs -I{} cp {} backup/
    • -0按\0切分,接find -print0或fd -0,文件名带空格也不炸
    • -P 4并行跑4个进程,-r空输入时不执行
    • 出身是findutils而非 coreutils,但总与它们同场出现
  • tee把流一分为二:写文件的同时继续往 stdout 传,-a追加,长管道中间插一个可现场观测数据

文件与校验

  • stat看完整元数据,-c自定义格式:stat -c '%n %s %y' *
  • readlink -f把符号链接解析到底层绝对路径,realpath是等价的独立命令
  • 校验清单:sha256sum * > sums生成,sha256sum -c sums批量校验,传完大文件的例行确认
  • split切大文件:-b 100M按字节、-l 10000按行、-d用数字后缀,cat part* > big合并
  • mktemp安全地创建临时文件或目录,别自己拼/tmp/foo.$$,并发冲突与符号链接攻击都会找上门
  • timeout 10s 命令给命令限时,超时发SIGTERM,-k 5s后补SIGKILL
  • install一步完成复制加权限设定,Makefile 常客:install -m 755 app /usr/local/bin
  • env:env VAR=x cmd带变量运行命令,env -i cmd清空环境做最小复现

相关阅读