简体   繁体   English

无法在Bash Shell脚本中打印整个阵列

[英]Cannot print entire array in Bash Shell script

I've written a shell script to get the PIDs of specific process names (eg pgrep python , pgrep java ) and then use top to get the current CPU and Memory usage of those PIDs. 我已经编写了一个Shell脚本来获取特定进程名称的PID(例如pgrep pythonpgrep java ),然后使用top来获取这些PID的当前CPU和内存使用情况。

I am using top with the '-p' option to give it a list of comma-separated PID values. 我将top与'-p'选项一起使用,以给它一个逗号分隔的PID值列表。 When using it in this mode, you can only query 20 PIDs at once, so I've had to come up with a way of handling scenarios where I have more than 20 PIDs to query. 在这种模式下使用它时,一次只能查询20个PID,所以我不得不想出一种方法来处理要查询的PID超过20个的情况。 I'm splitting up the list of PIDs passed to the function below and "despatching" multiple top commands to query the resources: 我正在拆分传递给以下函数的PID列表,并“分派”多个顶部命令以查询资源:

# $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'`

The top command will return the CPU and Memory usage for each PID in the format (CPU, MEM, CPU, MEM etc)...: top命令将以以下格式(CPU,MEM,CPU,MEM等)返回每个PID的CPU和内存使用率:

13 31.5 23 22.4 55 10.1

The problem is with the resourceUsage array. 问题出在resourceUsage数组上。 Say, I have 25 PIDs I want to process, the code above will place the results of the first 20 PIDs in to $resourceUsage[0] and the last 5 in to $resourceUsage[1] . 假设我要处理25个PID,上面的代码会将前20个PID的结果放入$resourceUsage[0] ,将最后5个PID的结果放入$resourceUsage[1] I have tested this out and I can see that each array element has the list of values returned from top. 我已经对此进行了测试,可以看到每个数组元素都有从顶部返回的值列表。

The next bit is where I'm having difficulty. 下一点是我遇到的困难。 Any time I've ever wanted to print out or use an entire array's set of values, I use ${resourceUsage[@]} . 每当我想打印或使用整个数组的一组值时,我都会使用${resourceUsage[@]} Whenever I use that command in the context of this script, I only get element 0's data. 每当我在此脚本的上下文中使用该命令时,我只会获取元素0的数据。 I've separated out this functionality in to a script below, to try and debug. 我在下面的脚本中分离了此功能,以进行尝试和调试。 I'm seeing the same issue here too (data output to debug.log in same dir as script): 我也在这里看到了相同的问题(数据输出到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"

I'm really stuck here as I've printed out entire arrays before in Bash Shell with no problem. 我真的被困在这里,因为之前我已经在Bash Shell中毫无问题地打印了整个数组。 I've even repeated this in the shell console with a few lines and it works fine there: 我什至在shell控制台中重复了几行,在这里可以正常工作:

listOfValues[0]="1 2 3 4"
listOfValues[1]="5 6 7 8"
echo "${listOfValues[@]}"

Am I missing something totally obvious? 我是否缺少一些显而易见的东西? Any help would be greatly appreciated! 任何帮助将不胜感激!

Thanks in advance! 提前致谢! :) :)

Welcome to StackOverflow, and thanks for providing a test case! 欢迎使用StackOverflow,并感谢您提供测试用例! The bash tag wiki has additional suggestions for creating small, simplified test cases. bash标签Wiki提供了有关创建小型简化测试用例的其他建议。 Here's a minimal version that shows your problem: 这是显示您的问题的最低版本:

log() {
  echo "$1"
}
array=(foo bar)
log "Values: ${array[@]}"

Expected: Values: foo bar . 预期Values: foo bar Actual: Values: foo . 实际: Values: foo

This happens because ${array[@]} is magic in quotes, and turns into multiple arguments. 发生这种情况是因为${array[@]}用引号引起来,并且变成了多个参数。 The same is true for $@ , and for brevity, let's consider that: 对于$@同样如此,为简便起见,让我们考虑一下:

Let's say $1 is foo and $2 is bar . 假设$1foo$2bar

  • The single parameter "$@" (in quotes) is equivalent to the two arguments "foo" "bar" . 单个参数"$@" (用引号引起来)等效于两个参数"foo" "bar"
  • "Values: $@" is equivalent to the two parameters "Values: foo" "bar" "Values: $@"等效于两个参数"Values: foo" "bar"

Since your log statement ignores all arguments after the first one, none of them show up. 由于您的日志语句将忽略第一个参数之后的所有参数,因此不会显示任何参数。 echo does not ignore them, and instead prints all arguments space separated, which is why it appeared to work interactively. echo不会忽略它们,而是将所有参数以空格分开打印,这就是为什么它看起来可以交互工作的原因。

This is as opposed to ${array[*]} and $* , which are exactly like $@ except not magic in quotes, and does not turn into multiple arguments. 这与${array[*]}$* ,它们与$@完全一样,除了引号不是魔术,并且不会变成多个参数。

  • "$*" is equivalent to "foo bar" "$*"等效于"foo bar"
  • "Values: $*" is equivalent to "Values: foo bar" "Values: $*"等效于"Values: foo bar"

In other words: If you want to join the elements in an array into a single string, Use * . 换句话说:如果要将数组中的元素合并为单个字符串,请使用* If you want to add all the elements in an array as separate strings, use @ . 如果要将数组中的所有元素添加为单独的字符串,请使用@

Here is a fixed version of the test case: 这是测试用例的固定版本:

log() {
  echo "$1"        
}
array=(foo bar)
log "Values: ${array[*]}"

Which outputs Values: foo bar 输出Values: foo bar

I would use ps , not top , to get the desired information. 我将使用ps而不是top来获取所需的信息。 Regardless, you probably want to put the data for each process in a separate element of the array, not one batch of 20 per element. 无论如何,您可能希望将每个进程的数据放在数组的单独元素中,而不是将每个元素的数据放入20个批中。 You can do this using a while loop and a process substitution. 您可以使用while循环和进程替换来执行此操作。 I use a few array techniques to simplify the process ID handling. 我使用一些数组技术来简化进程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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM