统计进程CPU使用率并按使用率从大到小排序

#!/bin/bash

# 文件名: show_cpu_usage.sh
# 作者: wanghongwei
# 日期: 2025年1月20日
# 版本: 1.0
# 描述: 统计进程 CPU 使用率并按使用率从大到小排序
# 使用方式:./show_cpu_usage.sh [-i interval]

# 初始化存储进程信息的数组
declare -A process_cpu_usage
declare -g temp_file=""

# 显示脚本使用方法
show_usage() {
    echo "Usage: $0 [-i interval]"
    echo "  -i interval: 采样时间间隔,单位为秒,默认为 1 秒。"
    exit 1
}

# 函数:检查输入是否为有效的正整数且不为 0
is_valid_number() {
    local num=$1
    if [[ $num =~ ^[1-9][0-9]*$ ]]; then
        return 0
    else
        return 1
    fi
}

# 函数:获取进程的 CPU 时间信息
get_process_cpu_time() {
    local process_id=$1
    local cpu_info=$(cat /proc/$process_id/stat 2>/dev/null)
    if [[ -z $cpu_info ]]; then
        echo "Error: Failed to read /proc/$process_id/stat"
        return 1
    fi
    # 解析 stat 文件中的信息
    # 格式为 pid comm state ppid pgrp session tty_nr tpgid flags minflt cminflt majflt cmajflt utime stime cutime cstime priority nice num_threads itrealvalue starttime vsize rss rsslim startcode endcode startstack kstkesp ksteip signal blocked sigignore sigcatch wchan nswap cnswap 0 0 1 2 3 4 5 6
    read -r pid comm state ppid pgrp session tty_nr tpgid flags minflt cminflt majflt cmajflt utime stime cutime cstime priority nice num_threads itrealvalue starttime vsize rss rsslim startcode endcode startstack kstkesp ksteip signal blocked sigignore sigcatch wchan nswap cnswap _ <<<"$cpu_info"
    local total_time=$((utime + stime + cutime + cstime))
    echo $total_time
    return 0
}

# 函数:计算 CPU 使用率
calculate_process_cpu_usage() {
    local process_id=$1
    local previous_time=$2
    local current_time=$(get_process_cpu_time $process_id)
    local elapsed_time=$(( $3 ))
    if [[ $elapsed_time -eq 0 ]]; then
        echo 0
    else
        local cpu_usage=$(echo "scale=2; (($current_time - $previous_time) / $elapsed_time)" | bc)
        echo $cpu_usage
    fi
}

# 获取进程的 CPU 使用率
get_cpu_usage() {
    # 存储上一次的 CPU 时间和时间戳
    declare -A previous_cpu_times
    declare -A previous_timestamps
    # 第一次读取
    for pid in /proc/[0-9]*; do
        pid=${pid##*/}
        previous_cpu_times[$pid]=$(get_process_cpu_time $pid)
        previous_timestamps[$pid]=$(date +%s)
    done
    # 等待一段时间,以便获取时间差
    sleep $1  # 使用传入的采样时间间隔
    # 第二次读取
    for pid in /proc/[0-9]*; do
        pid=${pid##*/}
        local curr_time=$(get_process_cpu_time $pid)
        local prev_time=${previous_cpu_times[$pid]}
        local prev_timestamp=${previous_timestamps[$pid]}
        local curr_timestamp=$(date +%s)
        local elapsed_time=$((curr_timestamp - prev_timestamp))
        if [[ -n $prev_time ]]; then
            local usage=$(calculate_process_cpu_usage $pid $prev_time $elapsed_time)
            process_cpu_usage[$pid]=$usage
        fi
    done
}

# 对 CPU 使用率进行排序并输出
sort_and_print() {
    # 将关联数组转换为适合排序的格式
    temp_file=$(mktemp)
    if [[ -z $temp_file ]]; then
        echo "Error: Failed to create temporary file."
        exit 1
    fi
    for pid in "${!process_cpu_usage[@]}"; do
        echo "${process_cpu_usage[$pid]} $pid" >> $temp_file
    done
    # 对文件中的内容进行排序
    sort -nr $temp_file | while read -r usage pid; do
        printf "| %-8s | %-20s |\n" "$pid" "${usage}%"
    done
    rm $temp_file
}

# 输出表格的标题
print_title() {
    echo "+----------+----------------------+"
    printf "| %-8s | %-20s |\n" "PID" "CPU Usage (%)"
    echo "+----------+----------------------+"
}

# 信号处理函数,用于在程序结束或异常终止时删除临时文件
cleanup() {
    rm -f $temp_file 2>/dev/null
}


main() {
    interval=1
    if [ $# -eq 2 ] && [ "$1" == "-i" ]; then
        if is_valid_number $2; then
            interval=$2
        else
            echo "Error: Invalid interval value. Please provide a positive integer greater than 0."
            show_usage
        fi
    elif [ $# -eq 1 ] && [ "$1" == "-i" ]; then
        echo "Error: Interval value is missing after -i."
        show_usage
    elif [ $# -ne 0 ]; then
        show_usage
    fi
    print_title
    get_cpu_usage $interval
    sort_and_print
    exit 0
}

# 注册信号处理函数
trap cleanup EXIT

main $1 $2
posted @   wanghongwei-dev  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
点击右上角即可分享
微信分享提示