nosforever nosforever - 10 days ago 5
Linux Question

Using SED to convert sensors command output to a loggable entry

Consider the following

sensor
command output:

amdgpu-pci-0100
temp1: +67.0°C crit = +0.0°C, hyst = +0.0°C

amdgpu-pci-0600
temp1: +59.0°C crit = +0.0°C, hyst = +0.0°C

amdgpu-pci-0700
temp1: +64.0°C crit = +0.0°C, hyst = +0.0°C

it8721-isa-0290
Vbat: +3.43 V
temp1: +43.0°C low = +97.0°C, high = -90.0°C ALARM sensor = thermistor
temp2: +40.0°C low = +79.0°C, high = +2.0°C ALARM sensor = thermistor
temp3: -128.0°C low = -128.0°C, high = +69.0°C sensor = disabled

k10temp-pci-00c3
temp1: +25.1°C high = +70.0°C
crit = +90.0°C, hyst = +87.0°C

fam15h_power-pci-00c4
power1: 13.04 W crit = 94.99 W

asus-isa-0000
cpu_fan: 0 RPM


I'm trying to turn this into a log entry, like this, where the "temp1" values are placed next to the PCI adapters, and the board (it8721-isa-0290):

<Date_Time> amdgpu-pci-0100 - +67.0°C | amdgpu-pci-0600 - +59.0°C | amdgpu-pci-0700 - +64.0°C | it8721-isa-0290 - +43.0°C


I think sed is the correct tool for this, but if it's not please let me know. I have already trimmed some data out using 2 sed commands, but I am not quite sure how to either delete out the rest that I don't need or match on just the stuff I do need. Some assistance would be greatly appreciated.

EDIT: Here's what I have so far as far as a command:

sensors | sed -e '/Adapter/d' -e '/in/d' -e 's/[)(]//g' -e '/Vbat/d' \
`-e 's/crit.*//' -e 's/low.*//' -e '/temp2/d' -e '/temp3/d' -e '/^k10temp-pci-`00c3$/,$d' \
-e 's/temp.://'


Here's the output (just need to add some separators):

amdgpu-pci-0100 +68.0°C amdgpu-pci-0600 +60.0°C amdgpu-pci-0700 +65.0°C it8721-isa-0290 +44.0°C


Is there an easier way to accomplish what I did?

Answer

The output of the sensors command heavily depends on the configuration file. For example, it is possible to rename the labels with a configuration similar to the following:

chip "as99127f-*"
  label temp1 "Mobo Temp"
  label temp2 "CPU0 Temp"

With such configuration the output of sensors command will look like this:

Mobo Temp:        +35.0°C  (high =  +0.0°C, hyst = -128.0°C)
CPU0 Temp:        +47.5°C  (high = +100.0°C, hyst = +75.0°C)

However, the sensors utility provides -u switch that prints an easy-to-parse raw data. In raw mode, the chip feature names are printed in standard formats such as temp1_input, temp1_max, temp1_crit, and temp1_crit_alarm, e.g.:

acpitz-virtual-0
Adapter: Virtual device
temp1:
  temp1_input: 64.000
  temp1_crit: 120.000
temp2:
  temp2_input: 30.000
  temp2_crit: 120.000

coretemp-isa-0000
Adapter: ISA adapter
Physical id 0:
  temp1_input: 67.000
  temp1_max: 87.000
  temp1_crit: 105.000
  temp1_crit_alarm: 0.000
Core 0:
  temp2_input: 67.000
  temp2_max: 87.000
  temp2_crit: 105.000
  temp2_crit_alarm: 0.000
  ...

The format is still not as easy to parse as it could be (think of JSON, or XML). The chip feature blocks are separated with newlines. Each block starts with the chip name. So you need to remember when the block starts, and print when the block ends (an empty line, or end of file).

It is possible to implement the algorithm in AWK. It is even possible to do this in SED. But I find these tools inconvenient for this case. I would rather use more flexible tool such as Perl (maybe I have just used to Perl more than to AWK).

Example

sensors-temp.pl

#!/usr/bin/perl
my $nl = 0;
my $chip;
my @t;

while (<>) {
  if ($chip eq "" or $nl) {
    ($chip = $_) =~ s/\n*$//;
    @t = ()
  } elsif ($_ =~ /temp\d+_input: ([\d\.]+)/m) {
     push @t, $1;
  }

  $nl = $_ eq "\n";

  # Average chip's temperature
  if ($nl or eof()) {
    my $t; map { $t += $_ } @t;
    printf("%s - %.1f°C\n", $chip, $t / (scalar @t))
  }
}

Usage

sensors -u | perl sensors-temp.pl

Sample Output

acpitz-virtual-0 - 47.5°C
coretemp-isa-0000 - 65.4°C

The script calculates average temperature for the temp\d+_input entries. You can easily change the script to fit our needs. For example, in order to print a list of all "input" temperatures for a chip, replace the last block in the loop with the following:

printf("%s - %s°C\n", $chip, join("|", map { sprintf("%.1f", $_) } @t))
  if $nl or eof();

Sample Output

acpitz-virtual-0 - 67.0|30.0°C
coretemp-isa-0000 - 68.0|67.0|63.0|66.0|61.0°C

P.S.

The code in the samples prints newlines for each chip for the sake of clarity. Replace the newlines (\n) with " | ", or whatever you find suitable for your case.