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

Shell脚本中$!返回‘not found’错误,如何正确传递PID?

解决Shell脚本监控进程时长、CPU及内存占用的PID错误问题

问题背景

想要监控编译命令的时长、CPU及内存占用,计划通过$!获取命令PID实现。用sleep测试脚本时,出现1209: not found、PID: not found等错误,尝试pgrep后问题依旧,需要解决变量传递和脚本错误。

测试脚本原代码:

#!/bin/sh
FILE_EXTENSION="$(date +\"%d-%m-%Y-%T\")"
TIME_FILE_NAME=outout/test_duration_$FILE_EXTENSION.txt
RESOURCES_FILE_NAME=output/test_internal_resources$FILE_EXTENSION.csv

monitorResources(){
    PID=$1
    echo "the pid i found is $($PID)"
    mkdir -p output
    echo "TIME_STAMP, Usage%, Memory Usage (MB)" > "$RESOURCES_FILE_NAME"
    TOTAL="$(free -m | grep Mem | tr -s ' ' | cut -d ' ' -f 2)"
    while ps -p "$PID" > /dev/null
    do 
        logResources "$PID" "$TOTAL"
    done
}

logResources(){
    PID=$1
    TOTAL=$2

    echo "logging procces $(PID)"
    DATE=$(date +\"%H:%M:%S:%s%:z\")
    echo "$DATE, " >> "$RESOURCES_FILE_NAME"
    top -b -n 1 -p "$PID" | tr -s ' ' | cut -d ' ' -f 10 >> "$RESOURCES_FILE_NAME"
    ##VAR="$(top -b -n 1 -p $PID | tr -s ' ' | cut -d ' ' -f 11)"
    ##echo "scale=3; ($VAR*$TOTAL/100)" | bc >> $RESOURCES_FILE_NAME

    sleep 1
}

##cd /php-src
##{ time make -j $(nproc) ; } > $TIME_FILE_NAME 2>&1 &
sleep 5 &
monitorResources $!

wait
cat "$TIME_FILE_NAME"
cat "$RESOURCES_FILE_NAME"

执行报错:

./run_test.sh: 1: 1209: not found
./run_test.sh: 1: the: not found

./run_test.sh: 22: PID: not found
logging procces
./run_test.sh: 22: PID: not found
logging procces

错误分析

  1. 错误的命令执行语法:$($PID) 和 $(PID) 是把变量内容当作命令执行,而非引用变量值。比如$($PID)会把PID数字(如1209)当成命令去运行,自然会报"1209: not found";$(PID)会尝试执行PID命令,所以报"PID: not found"。正确引用变量应该用$PID。
  2. 路径拼写错误:TIME_FILE_NAME中的outout是拼写错误,应为output,否则后续cat会找不到文件。
  3. 多余的转义引号:date +\"%d-%m-%Y-%T\"中的转义引号会导致文件名包含双引号,应该直接写date +"%d-%m-%Y-%T"(双引号内不需要转义)。
  4. top字段不可靠:不同系统的top输出字段位置可能不同,用ps获取进程资源更稳定。
  5. 内存计算未完善:注释的内存计算部分可以修复,实现实际内存占用计算。

修复后的脚本

#!/bin/sh
# 修复:去掉date命令的多余转义引号
FILE_EXTENSION="$(date +"%d-%m-%Y-%T")"
# 修复:路径拼写错误outout→output
TIME_FILE_NAME=output/test_duration_$FILE_EXTENSION.txt
RESOURCES_FILE_NAME=output/test_internal_resources_$FILE_EXTENSION.csv

monitorResources(){
    PID=$1
    # 修复:直接引用变量$PID,而非执行命令$($PID)
    echo "监控的PID是 $PID"
    mkdir -p output
    echo "TIME_STAMP, CPU_Usage%, Memory_Usage_MB" > "$RESOURCES_FILE_NAME"
    # 简化内存总容量获取
    TOTAL_MEM=$(free -m | awk '/Mem:/ {print $2}')
    while ps -p "$PID" > /dev/null
    do 
        logResources "$PID" "$TOTAL_MEM"
        sleep 1
    done
}

logResources(){
    PID=$1
    TOTAL_MEM=$2

    # 修复:引用变量$PID,而非执行命令$(PID)
    echo "正在记录进程 $PID 的资源使用"
    # 修复:去掉date命令的多余转义引号
    TIMESTAMP=$(date +"%H:%M:%S %z")
    # 用ps命令获取CPU和内存占比,比top更稳定
    # ps输出格式:%cpu %mem
    PROC_STATS=$(ps -p "$PID" -o %cpu=,%mem= | tr -s ' ')
    CPU_USAGE=$(echo "$PROC_STATS" | cut -d ' ' -f 1)
    MEM_PERCENT=$(echo "$PROC_STATS" | cut -d ' ' -f 2)
    # 计算实际内存占用(MB)
    MEM_USAGE=$(echo "scale=1; $MEM_PERCENT * $TOTAL_MEM / 100" | bc)
    # 将所有数据写入一行,避免换行问题
    echo "$TIMESTAMP, $CPU_USAGE, $MEM_USAGE" >> "$RESOURCES_FILE_NAME"
}

# 测试用sleep,替换为实际编译命令即可
sleep 5 &
# 传递PID给监控函数
monitorResources "$!"

wait
echo "=== 执行时长 ==="
cat "$TIME_FILE_NAME"
echo "=== 资源使用记录 ==="
cat "$RESOURCES_FILE_NAME"

关键修改说明

  • 所有变量引用统一使用$PID,杜绝把变量当作命令执行的错误。
  • 修正路径拼写和date命令的引号问题,避免文件名异常。
  • 用ps替代top获取进程资源,输出格式更稳定,跨系统兼容性更好。
  • 完善内存占用计算逻辑,直接输出一行完整数据,避免文件内容换行混乱。
  • 优化变量命名,增强可读性(如TOTAL→TOTAL_MEM,DATE→TIMESTAMP)。

内容的提问来源于stack exchange,提问作者Tibo G.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 09:15:32