自定义grepn函数适配df、cat却不适配ps的原因及修复
保留表头的grep工具在ps命令下的异常问题及修复
日常执行命令输出表格类内容时,用grep匹配目标行常会丢失表头,比如执行ps -aux | grep firefox查看Firefox进程PID时,无法直接知晓各列的含义。针对这个问题,我编写了grepn函数,它会保留表格前n行(默认1行)作为表头,再将剩余内容传给grep处理:
# 保留前n行(默认1行)表头,剩余内容通过grep匹配 grepn () { num_lines=1 # 解析-n参数 if [[ "$1" =~ -n(=.+)? ]]; then if [[ "$1" == *=* ]]; then num_lines="${1#*=}" else shift num_lines=$1 fi if [[ ! $num_lines =~ [0-9]+ ]]; then echo "无效的行数: ${num_lines}"; return -1 fi shift fi tee >(head -n $num_lines) >(tail -n +$((1+$num_lines)) | grep "$@") >/dev/null }
问题现象
- 处理
df -h的输出时正常,比如df -h | grepn sda1可以正常输出指定表头行和匹配grep的内容行; - 处理
ps -aux的输出时,仅能得到表头行,简化测试命令ps -aux | tee >(head -1) >(tail -n +2 | grep firefox) >/dev/null也会出现同样问题,仅输出:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
- 若先将
ps -aux的输出重定向到文件,再用cat将文件内容管道给grepn,则可以正常运行。
问题原因
核心原因是tee启动的两个子进程(head和tail+grep)是并行执行的:
head -n $num_lines读取完指定行数后会立即退出,导致tee收到SIGPIPE信号,直接终止后续输出;ps -aux的部分系统实现是分批次输出内容,当head退出触发tee终止时,后续的进程内容还没来得及被tail读取就被截断了;- 而
df -h是一次性输出所有内容后再退出,tee能在head退出前把所有内容传给两个子进程,因此不会出现截断问题。
修复方案
用逐行读取并输出表头的方式替代原有的并行tee方案,避免SIGPIPE提前截断输出。将原函数中tee开头的那一行代码替换为以下内容:
# 替换原函数中以tee开头的行 for (( i=0; i < num_lines; i++ )); do IFS= read -r line printf '%s\n' "$line" done grep "$@"
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

