3
\$\begingroup\$

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>/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
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

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 <<< "$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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.