You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 21:45:36