这个Ruby和shell代码有什么区别?

时间:2013-04-11 15:28:23

标签: ruby bash shell cpu cpu-usage

我有一个shell脚本以百分比计算CPU使用率。 因为我想扩展功能,并希望在Ruby中执行此操作,而不是从Ruby调用shell脚本。

我试图在Ruby中重写代码,但最终输出存在差异。

shell代码输出介于5%和10%之间,Ruby代码的输出介于97.5%和97.8%之间。

这是Ruby代码:

result = `cat /proc/stat | grep '^cpu '`.split(" ")
result.delete("cpu")
idle_time0 = result[4].to_i
total_time0 = 0

result.each do |partial_time|
  total_time0 += partial_time.to_i
end


sleep 0.5


result = `cat /proc/stat | grep '^cpu '`.split(" ")
result.delete("cpu")
idle_time = result[4].to_i
total_time = 0

result.each do |partial_time|
  total_time += partial_time.to_i
end

diff_idle = idle_time - idle_time0
diff_total = total_time - total_time0

diff_usage = (1000*(diff_total - diff_idle)/(diff_total+5).to_f)/10.0

p diff_usage

这是shell脚本:

#!/bin/bash

CPU=(`cat /proc/stat | grep '^cpu '`) # Get the total CPU statistics.
unset CPU[0]                          # Discard the "cpu" prefix.
IDLE=${CPU[4]}                        # Get the idle CPU time.

# Calculate the total CPU time.
TOTAL=0
for VALUE in "${CPU[@]}"; do
  let "TOTAL=$TOTAL+$VALUE"
done

# Remember the total and idle CPU times for the next check.
PREV_TOTAL="$TOTAL"
PREV_IDLE="$IDLE"

# Wait before checking again.
sleep 0.5

CPU=(`cat /proc/stat | grep '^cpu '`) # Get the total CPU statistics.
unset CPU[0]                          # Discard the "cpu" prefix.
IDLE=${CPU[4]}                        # Get the idle CPU time.

# Calculate the total CPU time.
TOTAL=0
for VALUE in "${CPU[@]}"; do
  let "TOTAL=$TOTAL+$VALUE"
done

# Calculate the CPU usage since we last checked.
let "DIFF_IDLE=$IDLE-$PREV_IDLE"
let "DIFF_TOTAL=$TOTAL-$PREV_TOTAL"
let "DIFF_USAGE=(1000*($DIFF_TOTAL-$DIFF_IDLE)/$DIFF_TOTAL+5)/10"

echo -en "\rCPU: $DIFF_USAGE%  \b\b"

2 个答案:

答案 0 :(得分:1)

问题在于,在bash中取消设置数组值会产生奇怪的效果:

CPU=(`cat /proc/stat | grep '^cpu '`) # Get the total CPU statistics.

let COUNT=0
while [ $COUNT -lt "${#CPU[@]}" ]; do
  echo "value at $COUNT: ${CPU[$COUNT]}"
  let "COUNT=$COUNT+1"
done

unset CPU[0]                          # Discard the "cpu" prefix.

let COUNT=0
while [ $COUNT -lt "${#CPU[@]}" ]; do
  echo "value at $COUNT: ${CPU[$COUNT]}"
  let "COUNT=$COUNT+1"
done

输出:

value at 0: cpu
value at 1: 763993
value at 2: 116443
value at 3: 179513
value at 4: 22344343
value at 5: 536446
value at 6: 5
value at 7: 640
value at 8: 0
value at 9: 0
value at 10: 0
value at 0:          # This should be 763993
value at 1: 763993   # and so on...
value at 2: 116443
value at 3: 179513
value at 4: 22344343
value at 5: 536446
value at 6: 5
value at 7: 640
value at 8: 0
value at 9: 0        # ...and the last 0 value is vanished!

无论如何,解决方案是减少ruby idle_time索引:

...
idle_time0 = result[3].to_i
...
idle_time = result[3].to_i
...

答案 1 :(得分:1)

ProGNOMmers已经指出了你的索引错误,但我想要注意的是,如你所做的那样转换为ruby几乎没有任何好处。你的ruby代码看起来像bash一样丑陋,而你仍然要两次炮轰以获得/proc/stat。通过使用ruby作为高级语言,您可以使其更具可读性,更不容易出错且更高效。

这是重写的示例。我已经做了一个小方法来将/ proc / stat行转换为具有有意义名称的结构,因此不再有阵列索引问题可以进入,并且始终清楚您正在引用哪个计时器值。我使用File::readlinesEnumerable#grep来读取proc文件系统而不必外壳。我使用printf格式来获得你似乎正在寻找的百分位舍入效果。

#!/usr/bin/env ruby

# http://man7.org/linux/man-pages/man5/proc.5.html
CpuTimes = Struct.new :user, :nice, :system, :idle, :iowait, :irq,
                      :softirq, :steal, :guest, :guest_nice, :total

def get_cpu_times
  parts = File.readlines('/proc/stat').grep(/^cpu /).first.split
  times = parts[1..-1].map(&:to_i)
  CpuTimes[ *times ].tap { |r| r[:total] = times.reduce(:+) }
end

c0 = get_cpu_times
sleep 0.5
c1 = get_cpu_times

idle  = c1.idle - c0.idle
total = c1.total - c0.total
usage = total - idle
printf "CPU: %.1f%%", 100.0 * usage / total
相关问题