如何加速zsh shell脚本中top命令的调用速度?
优化Oh-My-Zsh提示符脚本的运行速度
我在Oh-My-Zsh的提示符里运行一个脚本,用来展示磁盘占用、CPU负载、内存使用和电池状态,输出效果是这样的:
(68% disk used) (1.48[16] cores @ 8.7%) (16G used / 16G) 100% 🔌
但每次回车后要等好几秒才显示提示符,实在烦人。我知道top -l 1 -s 0单次运行连0.25秒都不到,肯定是脚本里重复调用了它,而且df、sysctl、pmset这些命令也被多次调用,拖慢了速度。原脚本如下:
#!/bin/zsh toplist=(top -l 1 -s 0) sysctllist=(sysctl hw) pmsetlist=(pmset -g batt) storagelist=(df -h) availablestorage=$($storagelist | grep "/dev/disk1s2" | awk '{print $2}' | sed 's/Gi//') availablestorage=$((availablestorage * 1.073741824)) usedstorage=$($storagelist | grep "/" | awk '{print $3}' | grep "Gi" | sed 's/Gi//' | awk '{ sum += $1 } END { print sum }') usedstorage=$((usedstorage * 1.073741824)) storagepercentused=$((100 * (usedstorage / availablestorage))) storagepercentusedrounded=$( echo "($storagepercentused)/1" | bc) loadavg=$($toplist | grep 'Load Avg' | awk '{print $3, $4, $5}' | tr -d , | awk '{print $2}') ncpu=$($sysctllist | grep ".ncpu" | grep -o -E '[1-9]+') percentidle=$($toplist | grep 'CPU usage:' | awk '{print $3, $5, $7}' | tr -d , | tr -d % | awk '{print $3}') memoryfree=$($toplist | grep 'PhysMem' | awk '{print $2}') totalmemory=$($sysctllist | grep .memsize | grep -oE '[1-9]+') batterypercent=""$($pmsetlist | grep "%" | awk '{print $3}' | tr -d \;)"%" power="" if ($pmsetlist | grep "Battery Power") >> /dev/null then power="🔋" elif ($pmsetlist | grep "AC Power") >> /dev/null then power="🔌" fi echo "($storagepercentusedrounded%% disk used) ("$loadavg"["$ncpu"] cores @ "$(printf '%.*f ' 1 $((100 - $percentidle)))"%%) ("$memoryfree" used / "$(($totalmemory / 1024 / 1024 / 1024))"G)" %F{white}$batterypercent $power
核心问题与优化方案
你的脚本慢的根源是重复调用外部命令:top被调用了3次,df、sysctl、pmset各被调用了2次。每次启动外部进程都有额外开销,积少成多就导致了延迟。
优化思路是:每个外部命令只执行一次,把输出存入变量,后续直接从变量中提取所需信息;同时简化文本处理流程,减少不必要的管道和命令调用。
优化后的脚本:
#!/bin/zsh # 仅调用一次所有外部命令,将输出存入变量复用 top_output=$(top -l 1 -s 0) sysctl_output=$(sysctl hw) pmset_output=$(pmset -g batt) df_output=$(df -h) # 用单段awk完成磁盘信息的提取、求和与百分比计算 storage_percent=$(echo "$df_output" | awk ' /\/dev\/disk1s2/ { avail = $2; sub(/Gi/, "", avail) } /\// && $3 ~ /Gi/ { used += $3; sub(/Gi/, "", $3) } END { avail_gb = avail * 1.073741824 used_gb = used * 1.073741824 printf "%.0f", 100 * (used_gb / avail_gb) }') # 从单次top输出中提取所有需要的CPU、内存、负载信息 load_avg=$(echo "$top_output" | awk '/Load Avg/ { gsub(/,/, "", $4); print $4 }') cpu_idle=$(echo "$top_output" | awk '/CPU usage:/ { gsub(/[%|,]/, "", $7); print $7 }') mem_used=$(echo "$top_output" | awk '/PhysMem/ { print $2 }') # 从单次sysctl输出中提取CPU核心数和总内存 cpu_cores=$(echo "$sysctl_output" | awk '/hw.ncpu/ { print $2 }') total_mem=$(echo "$sysctl_output" | awk '/hw.memsize/ { print int($2 / 1024 / 1024 / 1024) }') # 单次提取电池百分比和供电状态 battery_percent=$(echo "$pmset_output" | awk '/%/ { gsub(/;/, "", $3); print $3 "%"}') power_icon=$(echo "$pmset_output" | awk '/Battery Power/ { print "🔋" } /AC Power/ { print "🔌" }') # 拼接最终输出 echo "($storage_percent% disk used) ($load_avg[$cpu_cores] cores @ $(printf "%.1f" $((100 - cpu_idle)))%) ($mem_used used / $total_memG) %F{white}$battery_percent $power_icon"
关键优化点
- 减少外部命令调用:所有系统命令只执行一次,避免重复启动进程的开销
- 简化文本处理:用单段awk替代多管道的
grep+sed+awk组合,减少进程启动次数 - 合并逻辑判断:电池状态直接用awk完成匹配输出,替代原来的分支判断+重复调用pmset
- 简化单位转换:总内存计算直接在awk中完成,避免后续的shell算术运算
内容的提问来源于stack exchange,提问作者Jacob Jackson
相关产品推荐
相关产品推荐

