当前位置: 首页 > news >正文

浅谈 awk 中管道的用法

把“文本”变成“进程”,再把“进程”变回“文本”,只用一条 awk 语句。


1 为什么需要管道

awk 本身是一门面向“行”的语言,默认逐行读取输入。
但在实际脚本里,我们经常需要:

  • 把当前行送给外部命令(rev、sort、grep、sqlite …)
  • 读取外部命令的输出做后续计算
  • 与系统工具链协同,而不用离开 awk 进程

这些需求靠“管道”完成,awk 提供了两条路:
a) 隐式管道(print | cmd / cmd | getline
b) 显式 system()(一次性执行)

两者对比如下表:

需求用 system()用隐式管道
启动一次外部命令
持续读写数据流
避免多余 /bin/sh
及时 flush/close手动自动+手动

不难看出,隐式管道更轻量、更可控,这也是本文的重点。


2 awk 管道的三种语法形态

以下语法基于 POSIX 标准,适用于主流 awk 实现(gawk、mawk、nawk)。

2.1 输出型管道:把 awk 的数据喂给外部进程

print [items] | "shell-cmd"
printf fmt, items | "shell-cmd"

2.2 输入型管道:把外部进程的输出读进 awk

"shell-cmd" | getline          # 读到 $0, NF, NR 被更新
"shell-cmd" | getline var      # 只读到变量 var

2.3 双工型:同时读写(高级用法,gawk 扩展)

"coprocess" |& print ...      # 写到协程
"coprocess" |& getline ...    # 从协程读

(本文不展开)

对比

形式流向说明
`print items“shell-cmd”`awk → 外部
`“shell-cmd”getline`外部 → awk
`“shell-cmd”getline var`外部 → awk
`“coproc”& getline`双向

3 实战 10 连击

以下是 10 个精心设计的实战案例,覆盖从简单到复杂的场景,展示 awk 管道的灵活性和强大功能。

3.1 奇偶行反转

awk 'NR%2 {print | "rev"; close("rev"); next} 1' file

说明:奇数行通过 rev 反转字符顺序,偶数行保持不变。close("rev") 确保每次调用后关闭管道,避免资源泄漏。

3.2 实时 DNS 解析

awk '{ cmd="dig +short " $1; cmd | getline ip; close(cmd); print $1, ip }' access.log

说明:从 access.log 中提取域名,通过 dig 获取 IP 地址并输出。close(cmd) 防止管道堆积。

3.3 每 1000 行排序一次

{buf = buf $0 "\n"
}
NR % 1000 == 0 {print buf | "sort"close("sort")buf = ""
}
END {if (buf) { print buf | "sort"; close("sort") }
}

说明:累积 1000 行数据后通过 sort 排序输出,END 块处理剩余不足 1000 行的数据。

3.4 HMAC-SHA256 每行计算

awk -v k='secret' '{cmd = "openssl dgst -sha256 -hmac " kprintf "%s", $0 | cmdclose(cmd)cmd | getline hclose(cmd)print $0 " -> " h
}' input.txt

说明:为每行文本计算 HMAC-SHA256 签名,适合校验数据完整性。

3.5 并行 8 路任务

BEGIN {for (i=1; i<=8; i++) print i | "xargs -P8 -I{} sh -c '\''sleep {}; echo {} done'\''"
}

说明:启动 8 个并行任务,模拟并发处理场景,适合批量任务调度。

3.6 持续读取 ps 输出

BEGIN {while ("ps -eo pid,pcpu --no-headers" | getline) cpu[$1] = $2for (pid in cpu) print pid, cpu[pid]
}

说明:实时监控进程的 CPU 使用率,存储在数组中并输出。

3.7 并发 curl 下载

{cmd = "curl -s " $1cmd | getline bodyclose(cmd)print $1, length(body)
}' urls.txt

说明:从 urls.txt 读取 URL,逐行调用 curl 下载内容并输出长度。

3.8 AWK 作为 Kafka Producer

awk '{ print $0 | "kcat -b broker:9092 -t topic" }' app.log

说明:将日志实时发送到 Kafka 的指定主题,适合日志流处理。

3.9 交互式 bc 计算器

BEGIN {cmd = "bc -l"for (i=1; i<=5; i++) {print i "/3" | cmdcmd | getline ansprint i, "÷ 3 =", ans}close(cmd)
}

说明:与 bc 交互,计算 1 到 5 除以 3 的结果,展示管道的双向交互能力。

3.10 动态 JSON 解析与统计

{cmd = "jq -c '{ts: .timestamp, code: .status}'"print $0 | cmdcmd | getline jsonclose(cmd)print json
}' app.log

说明:将日志行通过 jq 解析为 JSON 格式,提取 timestampstatus 字段,适合实时日志处理。


4 getline 返回值与错误处理

getline 的返回值需特别注意:

返回值含义
1成功读取一行
0文件末尾(EOF)
-1系统错误(如命令失败)
-2中断(如管道被关闭)

稳健模板

if (("cmd" | getline var) <= 0) {print "Error: Failed to execute cmd" > "/dev/stderr"next
}

优化建议

  • 总是检查 getline 返回值,避免因命令失败导致逻辑错误。
  • 使用 close(cmd) 及时关闭管道,释放资源。

5 Shell 元字符逃逸

外部命令中可能包含危险字符,需妥善处理:

字符风险解决方法
空格参数拆分"\"" $0 "\""
\, $, "被 shell 解析sprintf("%q", $0)
NULL 字节命令截断使用 base64 编码

安全示例:在 /etc/passwd 中搜索用户输入:

cmd = "grep -F -- " sprintf("%q", $0) " /etc/passwd"
cmd | getline result
close(cmd)
print result

6 性能与死锁

6.1 常见问题与解决

问题现象解决方法
Fork 风暴过多子进程耗尽 ulimit批量处理后统一 close()
缓冲死锁脚本挂起使用 fflush("")close()
行缓冲延迟实时性差使用 stdbuf -oL cmdunbuffer

6.2 优化技巧

  • 减少管道创建:缓存多行数据后一次性送往外部命令(如案例 3.3)。
  • 异步处理:对于高并发场景,使用 xargs -P 或后台进程(cmd &)。
  • 缓冲管理:显式调用 fflush("") 确保数据及时送达外部命令。

示例:优化高频管道调用

{buf = buf $0 "\n"if (++count % 100 == 0) {print buf | "grep pattern"close("grep pattern")buf = ""}
}
END {if (buf) { print buf | "grep pattern"; close("grep pattern") }
}

7 底层实现(gawk 9.x)

了解 awk 管道的底层机制有助于调试和优化:

  1. 解析阶段
    • print | "cmd" 解析为 N_POPEN 节点。
    • "cmd" | getline 解析为 N_GETLINE 节点。
  2. 运行阶段
    • 调用 popen("cmd", mode),触发 fork()pipe(),最终通过 execve("/bin/sh", "-c", "cmd") 执行命令。
  3. 资源回收
    • pclose(fd) 调用 waitpid(),清理子进程并移除 IO 表条目。
  4. 双工管道(coproc)
    • 使用 socketpair()dup2() 实现双向通信。

9 总结

awk 里的字符串不是命令,但一旦出现在 | "string""string" | getline 右侧,就立刻被提升为独立子进程的标准输入/输出。
掌握这条规则,你就能用几十个字节写出别人几十行脚本才能完成的流水线。

http://www.dtcms.com/a/284419.html

相关文章:

  • zynq mpsoc switch级联ssd高速存储方案
  • 贴吧项目总结二
  • mysql——搭建MGR集群
  • CommonJS 功能介绍
  • 基于dcmtk的dicom工具 第二章 图像接受StoreSCP(2)
  • Python Day16
  • Java行为型模式---备忘录模式
  • 从零开始的云计算生活——第三十三天,关山阻隔,ELK日志分析
  • rtp传输推流h265
  • Unity使用GTCRN实现流式语音增强
  • SpringBoot一Web Flux、函数式Web请求的使用、和传统注解@Controller + @RequestMapping的区别
  • 探微“元宇宙”:概念内涵、形态发展与演变机理
  • CSS面试题及详细答案140道之(41-60)
  • Kiro AI IDE上手初体验!亚马逊出品,能否撼动Cursor的王座?
  • Amazon S3成本优化完全指南:从入门到精通
  • 8 几何叠加分析
  • 系统设计时平衡超时时间与多因素认证(MFA)带来的用户体验下降
  • 量子计算的安全与伦理:当技术革命叩击数字时代的潘多拉魔盒
  • sqli-labs靶场通关笔记:第25-26a关 and、or、空格和注释符多重过滤
  • 4G模块 A7680通过MQTT协议连接到腾讯云
  • AI赋能Baklib,重塑企业知识管理与客户支持方式
  • Curr. Res. Food Sci.|福州大学吕旭聪团队:富硒鼠李糖乳杆菌GG重塑肠-肝轴,显著缓解酒精性肝损伤
  • 网络通信之基础知识
  • deep learning(李宏毅)--(六)--loss
  • day19-四剑客与正则-特殊符号正则-awk
  • [yotroy.cool] 记一次 Git 移除某个不该提交的文件
  • iOS WebView 调试与性能优化 跨平台团队高效协作方法解析
  • PyTorch生成式人工智能(18)——循环神经网络详解与实现
  • 可视化图解算法56:岛屿数量
  • Word 中为什么我的图片一拖就乱跑,怎么精确定位?