1

I'm using the command line tool from Temperature Monitor, the mac software, which looks like this:

$ /Applications/TemperatureMonitor.app/Contents/MacOS/tempmonitor -c -l -a


SMART Disk Hitachi HTS547550A9E384 (J2250050GMBY3C): 30 C
SMART Disk TOSHIBA MK5065GSXF (71SNCPW4T): 28 C
SMC BATTERY: 30 C
SMC BATTERY POSITION 2: 31 C
SMC BATTERY POSITION 3: 28 C
SMC CPU A DIODE: 47 C
SMC CPU A PROXIMITY: 45 C
SMC GPU 1 CHIP: 40 C
SMC LEFT PALM REST: 28 C
SMC MAIN HEAT SINK 2: 38 C
SMC MAIN HEAT SINK 3: 37 C
SMC MAIN LOGIC BOARD: 36 C
SMC PLATFORM CONTROLLER HUB: 49 C
SMC SSD BAY: 36 C

I want to clean this up a bit. So for example, let's say I want to get the average of the three Battery temperature readings. I thought of piping into grep for Battery, then awking through all the fields for the correct data, but that seems really messy.

So I want the three variables $BATTERY_1, $BATTERY_2, and $BATTERY_3 to have the content 30, 31, and 28 respectively.

Any suggestions on the cleanest way to do so?

JShoe
  • 3,186
  • 9
  • 38
  • 61

2 Answers2

3

It will be easier to create an array and then move the values from the array into the plain variables. It is trivial to do the extraction with awk:

TEMPMON="/Applications/TemperatureMonitor.app/Contents/MacOS/tempmonitor"
battery=( $("$TEMPMON" -c -l -a | awk '/BATTERY/ { print $(NF-1) }') )
BATTERY_1=${battery[0]}
BATTERY_2=${battery[1]}
BATTERY_3=${battery[2]}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Awesome. Quick question though, say I wanted to go through the same process with the Disks (which I am currently doing), I'd rather not have to run the program again, and when I try saving the output to a variable, and then awking that variable, it doesn't give me the correct output. Is there a way around this? Because I'm currently running the program 7 times, only to get data I just finished throwing away, and I just feel... dirty. – JShoe Jun 25 '14 at 00:59
  • The simplest thing is to run the command once, saving the output to file. You can then process the file multiple times (7 times, or as often as you need) using `awk` or any other tool. Alternatively, you can decide to have `awk` print more values, and use the array indices to save the values into the correct variables. Or you can have `awk` do the calculations and formatting; it is perfectly capable of computing averages and printing the data in most formats you're likely to want. – Jonathan Leffler Jun 25 '14 at 01:45
  • Yeah, but when I save the result once to a variable, it gets rid of the new lines, and when I awk the data, it only takes the last instance. – JShoe Jun 25 '14 at 02:23
  • 1
    No, it doesn't. See [Capturing multiple line output to a Bash variable](http://stackoverflow.com/questions/613572/capturing-multiple-line-output-to-a-bash-variable/613580#613580). – Jonathan Leffler Jun 25 '14 at 02:25
  • Thank you, you've been so much help! – JShoe Jun 25 '14 at 02:28
3

To complement @Jonathan Leffler's helpful answer:

If you don't actually need the individual values and instead want the average only, try:

... | awk '/ BATTERY/ {sum+=$(NF-1); ++i} END {OFMT="%.2f"; print sum / i}'
  • OFMT="%.2f" sets the (printf-style) output number format to 2 decimal places, resulting in 29.67.

Update: The OP, in a comment, asks for output in the format <Item name>: <avg temp> (<temp 1>, <temp 2>, <temp 3>):

... | awk -v itm='BATTERY' '
  $0 ~ itm {
    vals = vals (i ? " " : "") $(NF-1)
    sum += $(NF-1); ++i
  } 
  END {
    printf "%s: %.2f (%s)\n", itm, sum / i, vals
  }'
  • -v itm='BATTERY' passes the name of the items to match as awk variable itm.
  • $0 ~ itm matches (~) the current input line ($0) against itm (interpreted as a regular expression, which in this simple case performs substring matching).
  • awk splits input lines into fields $1, $2, ... by whitespace by default, and stores the number of fields in special variable NF. Since the values in the input data are in the next-to-last field, $(NF-1) references each line's value.
  • vals = ... builds up a string list of matching values; note how merely placing strings and variables next to each other causes them to be concatenated (as strings).
    • (i ? " " : "") is a C-style ternary conditional that returns a single space if condition i is true (i.e., if variable i has a nonzero value), and an empty string otherwise. In other words: if the value is not the first one, append a space before appending the value to the list of values built up so far. Note that uninitialized variables in awk default to an empty string in a string context, and 0 (false) in a numeric/Boolean context.
  • sum += ... sums up the values; ++i keeps the count of values.
  • END is a special pattern whose associated action (block) is processed after all input lines.
  • printf, for output based on a format (template) string, works like its C counterpart, and in this case outputs the item name (1st %s, instantiated with itm), the average with 2 decimal places (%.2f, instantiated with sum / i) and the list of values (last %s, instantiated with vals).
Community
  • 1
  • 1
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • That's awesome. I seem to know nothing about Awk. Do you have any good resources you'd recommend? I keep needing it. I've recently gotten into Geektool, and I can't stop... – JShoe Jun 25 '14 at 01:00
  • And I am also interested in the individual values. My format is : (, , ), in case one item is way higher, and I'm missing a potential problem. Kind of defeats the purpose. – JShoe Jun 25 '14 at 01:03
  • 1
    @JShoe: I have no personal recommendation, I'm afraid, other than reading the `man` page and studying answers here on SO - there are many `awk` experts here. Also note that there are different implementations that implement different extensions to the POSIX standard; GNU `awk` is the most powerful. Here's the POSIX awk manual page: http://man.cx/awk – mklement0 Jun 25 '14 at 02:36
  • @JShoe Re `: (, , )`: see my update - hopefully, the explanation is helpful. – mklement0 Jun 25 '14 at 02:52
  • Um... Sure. That looks good. I'm completely lost, and I think I'll stick with what I've got. Thanks so much though! – JShoe Jun 25 '14 at 03:18
  • 1
    @JShoe. You're welcome; I've made the explanation more detailed, perhaps that helps. If you really want to learn how to use `awk`, this could be a good starting point, because it contains several standard awk idioms - and the fact that you know what it does should help with studying it. – mklement0 Jun 25 '14 at 03:38
  • loved the way you explained! +1 – Devesh Jun 25 '14 at 03:51