我编写了一个shell脚本来获取特定进程名称的PID(例如pgrep python
,pgrep java
),然后使用top来获取这些PID的当前CPU和内存使用情况。
我正在使用' -p'选项为其提供逗号分隔的PID值列表。在此模式下使用它时,您一次只能查询20个PID,因此我必须想出一种方法来处理我需要查询超过20个PID的场景。我将分配传递给下面函数的PID列表和"发送"多个top命令来查询资源:
# $1 = List of PIDs to query
jobID=0
for pid in $1; do
if [ -z $pidsToQuery ]; then
pidsToQuery="$pid"
else
pidsToQuery="$pidsToQuery,$pid"
fi
pidsProcessed=$(($pidsProcessed+1))
if [ $(($pidsProcessed%20)) -eq 0 ]; then
debugLog "DESPATCHED QUERY ($jobID): top -bn 1 -p $pidsToQuery | grep \"^ \" | awk '{print \$9,\$10}' | grep -o '.*[0-9].*' | sed ':a;N;\$!ba;s/\n/ /g'"
resourceUsage[$jobID]=`top -bn 1 -p "$pidsToQuery" | grep "^ " | awk '{print $9,$10}' | grep -o '.*[0-9].*' | sed ':a;N;$!ba;s/\n/ /g'`
jobID=$(($jobID+1))
pidsToQuery=""
fi
done
resourceUsage[$jobID]=`top -bn 1 -p "$pidsToQuery" | grep "^ " | awk '{print $9,$10}' | grep -o '.*[0-9].*' | sed ':a;N;$!ba;s/\n/ /g'`
top命令将以格式(CPU,MEM,CPU,MEM等)返回每个PID的CPU和内存使用情况......:
13 31.5 23 22.4 55 10.1
问题在于resourceUsage
数组。比如,我想要处理25个PID,上面的代码会将前20个PID的结果放入$resourceUsage[0]
,将最后5个结果放入$resourceUsage[1]
。我已经测试了这一点,我可以看到每个数组元素都有从顶部返回的值列表。
下一点是我遇到困难的地方。每当我想要打印或使用整个数组的值时,我都会使用${resourceUsage[@]}
。每当我在这个脚本的上下文中使用该命令时,我只获得元素0的数据。我已将此功能分离到下面的脚本中,以尝试调试。我也在这里看到同样的问题(数据输出到与脚本相同的目录中的debug.log):
#!/bin/bash
pidList="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25"
function quickTest() {
for ((i=0; i<=1; i++)); do
resourceUsage[$i]=`echo "$i"`
done
echo "${resourceUsage[0]}"
echo "${resourceUsage[1]}"
echo "${resourceUsage[@]}"
}
function debugLog() {
debugLogging=1
if [ $debugLogging -eq 1 ]; then
currentTime=$(getCurrentTime 1)
echo "$currentTime - $1" >> debug.log
fi
}
function getCurrentTime() {
if [ $1 -eq 0 ]; then
echo `date +%s`
elif [ $1 -eq 1 ]; then
echo `date`
fi
}
jobID=0
for pid in $pidList; do
if [ -z $pidsToQuery ]; then
pidsToQuery="$pid"
else
pidsToQuery="$pidsToQuery,$pid"
fi
pidsProcessed=$(($pidsProcessed+1))
if [ $(($pidsProcessed%20)) -eq 0 ]; then
debugLog "DESPATCHED QUERY ($jobID): top -bn 1 -p $pidsToQuery | grep \"^ \" | awk '{print \$9,\$10}' | grep -o '.*[0-9].*' | sed ':a;N;\$!ba;s/\n/ /g'"
resourceUsage[$jobID]=`echo "10 10.5 11 11.5 12 12.5 13 13.5"`
debugLog "Resource Usage [$jobID]: ${resourceUsage[$jobID]}"
jobID=$(($jobID+1))
pidsToQuery=""
fi
done
#echo "Dispatched job: $pidsToQuery"
debugLog "DESPATCHED QUERY ($jobID): top -bn 1 -p $pidsToQuery | grep \"^ \" | awk '{print \$9,\$10}' | grep -o '.*[0-9].*' | sed ':a;N;\$!ba;s/\n/ /g'"
resourceUsage[$jobID]=`echo "14 14.5 15 15.5"`
debugLog "Resource Usage [$jobID]: ${resourceUsage[$jobID]}"
memUsageInt=0
memUsageDec=0
cpuUsage=0
i=1
debugLog "Row 0: ${resourceUsage[0]}"
debugLog "Row 1: ${resourceUsage[1]}"
debugLog "All resource usage results: ${resourceUsage[@]}"
for val in ${resourceUsage[@]}; do
resourceType=$(($i%2))
if [ $resourceType -eq 0 ]; then
debugLog "MEM RAW: $val"
memUsageInt=$(($memUsageInt+$(echo $val | cut -d '.' -f 1)))
memUsageDec=$(($memUsageDec+$(echo $val | cut -d '.' -f 2)))
debugLog " MEM INT: $memUsageInt"
debugLog " MEM DEC: $memUsageDec"
elif [ $resourceType -ne 0 ]; then
debugLog "CPU RAW: $val"
cpuUsage=$(($cpuUsage+$val))
debugLog "CPU TOT: $cpuUsage"
fi
i=$(($i+1))
done
debugLog "$MEM DEC FINAL: $memUsageDec (pre)"
memUsageDec=$(($memUsageDec/10))
debugLog "$MEM DEC FINAL: $memUsageDec (post)"
memUsage=$(($memUsageDec+$memUsageInt))
debugLog "MEM USAGE: $memUsage"
debugLog "CPU USAGE: $cpuUsage"
debugLog "MEM USAGE: $memUsage"
debugLog "PROCESSED VALS: $cpuUsage,$memUsage"
echo "$cpuUsage,$memUsage"
我真的被困在这里,因为我在Bash Shell之前打印完整个数组没有任何问题。我甚至在shell控制台中用几行重复了这一点,它在那里工作正常:
listOfValues[0]="1 2 3 4"
listOfValues[1]="5 6 7 8"
echo "${listOfValues[@]}"
我错过了一些完全明显的东西吗?任何帮助将不胜感激!
提前致谢! :)
答案 0 :(得分:5)
欢迎使用StackOverflow,感谢您提供测试用例! bash tag wiki提供了有关创建小型简化测试用例的其他建议。这是一个显示您问题的最小版本:
log() {
echo "$1"
}
array=(foo bar)
log "Values: ${array[@]}"
预期:Values: foo bar
。实际:Values: foo
。
这是因为${array[@]}
在引号中是神奇的,并且变成了多个参数。 $@
也是如此,为简洁起见,我们考虑一下:
我们说$1
是foo
而$2
是bar
。
"$@"
(引号中)相当于两个参数"foo"
"bar"
。 "Values: $@"
相当于两个参数"Values: foo"
"bar"
由于您的日志语句会忽略第一个之后的所有参数,因此不会显示任何参数。 echo
不会忽略它们,而是打印所有分隔的参数空间,这就是它似乎以交互方式工作的原因。
这与${array[*]}
和$*
相反,$@
与"$*"
完全相同,但引号中不是魔术,并且不会变成多个参数。
"foo bar"
相当于"Values: $*"
"Values: foo bar"
相当于*
换句话说:如果要将数组中的元素连接成单个字符串,请使用@
。如果要将数组中的所有元素添加为单独的字符串,请使用log() {
echo "$1"
}
array=(foo bar)
log "Values: ${array[*]}"
。
以下是测试用例的固定版本:
Values: foo bar
哪个输出{{1}}
答案 1 :(得分:1)
我会使用ps
而非top
来获取所需信息。无论如何,您可能希望将每个进程的数据放在数组的单独元素中,而不是每个元素20个批处理。您可以使用while
循环和进程替换来执行此操作。我使用一些数组技术来简化进程ID处理。
pid_array=(1 2 3 4 5 6 7 8 9 ... )
while (( ${#pid_array[@]} > 0 )); do
printf -v pidsToQuery "%s," "${pid_array[@]:0:20}"
pid_array=( "${pid_array[@]:20}" )
while read cpu mem; do
resourceUsage+=( "$cpu $mem" )
done < <( top -bn -1 -p "${pidsToQuery%,}" ... )
done