zsh system status script The following script is being used to generate the status line in wmii. I'm curious if there are faster/more efficient ways to find the various info I'm grabbing. I'm also a relative newbie when it comes to zsh (and shell scripting in general) so I'd also suspect that there are better ways to process some of the data.

#!/bin/zsh
cpu_freq=$(grep "cpu MHz" /proc/cpuinfo | sed 's/[\t ]*//g; s/cpuMHz://g')

cpu0=$(echo "scale=2;" `echo $cpu_freq | head -n 1 | tail -n 1` / 1000 | bc)
cpu1=$(echo "scale=2;" `echo $cpu_freq | head -n 2 | tail -n 1` / 1000 | bc)
cpu2=$(echo "scale=2;" `echo $cpu_freq | head -n 3 | tail -n 1` / 1000 | bc)
cpu3=$(echo "scale=2;" `echo $cpu_freq | head -n 4 | tail -n 1` / 1000 | bc)

stat=$(mpstat | tail -n 1 | tr -s ' ')
usr=$(echo $stat | cut -d ' ' -f 4)
nice=$(echo $stat | cut -d ' ' -f 5)
sys=$(echo $stat | cut -d ' ' -f 6)
cpu=$(echo $usr + $nice + $sys | bc)

memline=$(free | head -n 2 | tail -n 1 | tr -s ' ')
cached=$(echo $memline | cut -d ' ' -f 7)
free=$(echo $memline | cut -d ' ' -f 4)
cachedfree=$(echo $cached + $free | bc)
mem=$(echo "scale=3;" $cachedfree / 1000000 | bc)

network=$(nmcli nm | sed 's/  */\;/g' | cut -d \; -f 2 | tail -n 1)

tun0=$(ifconfig tun0 2&gt;/dev/null)
vpn=$(if [[ -n $tun0 ]]; then; echo "(vpn:" $(cat /tmp/current-vpn)")"; else; echo ""; fi)

battery=$(acpi -b | sed 's/Battery 0: //; s/ until charged//; s/ remaining//; s/Charging/C/; s/Discharging/D/; s/Full, //; s/Unknown, //')

load=$(uptime | sed 's/.*://; s/,//g')

date=$(date +%a\ %b\ %d\ %H:%M)

echo -n $vpn $network '||' $mem GB '||' $cpu% '('$cpu0 $cpu1 $cpu2 $cpu3') ||' $load '||' $date '||' $battery
Extracting a column

It's a lot easier to extract columns from text using awk.
Instead of this:


grep "cpu MHz" /proc/cpuinfo | sed 's/[\t ]*//g; s/cpuMHz://g'



This is shorter, and uses a single awk process instead of grep + sed:

awk '/cpu MHz/ {print $4}' /proc/cpuinfo


Extracting a line

This is a really awkward method to extract the 4th line:


echo $cpu_freq | head -n 4 | tail -n 1



This is simpler and better:

sed -ne 4p &lt;&lt;&lt; "$cpu_freq"


Using arrays

Instead of getting the values of cpu0..cpu3 one by one by running separate command(s) for each, you can get it in one go:

cpus=($({ echo scale=2; awk '/cpu MHz/ {print $4 " / 1000"}' /proc/cpuinfo; } | bc))


Explanation:


The awk command appends the " / 1000" to the CPU frequency, so it's ready for piping to bc
Pipe all lines (commands) to bc at once, and get all the output at once
Group the echo scale=2 and the awk for bc by enclosing in { ...; }
Store the output of bc in an array


From this array, you can extract the individual CPU values

cpu0=${cpus[0]}
cpu1=${cpus[1]}
cpu2=${cpus[2]}
cpu3=${cpus[3]}


Next steps

I don't want to spoil all the fun for you :-)
Following the techniques above,
you should be able to extract the other values more efficiently (using fewer processes) in a similar fashion.