awk question

Polytropon freebsd at edvax.de
Mon Oct 5 04:34:15 UTC 2015


On Sun, 4 Oct 2015 23:12:48 -0453.75, William A. Mahaffey III wrote:
> On 10/04/15 21:27, Polytropon wrote:
> > On Sun, 4 Oct 2015 19:55:08 -0453.75, William A. Mahaffey III wrote:
> >> I am using awk & smartctl in a small shell script to print out HDD temps
> >> in a purty format, 1 line per drive. As it happens, the output I want is
> >> spread out over 4 lines of smartctl out, requiring (I *think*) 4 calls
> >> to smartctl each piped to its own awk invocation to pull out the line I
> >> want & print its info out. Is there some way to get awk to consider more
> >> than 1 line at a time ? In my case my 4 lines are indeed sequential, &
> >> it would be a bit more efficient if I could process all 4 lines once I
> >> found the 1st one. This is definitely *not* critical, what I have now
> >> works AOK, I was/am just curious if it could be optimized a bit. TIA &
> >> have a good one.
> > I'm not sure I understand your question correctly, as you're not
> > providing some example input data and what output you want. But
> > awk can process one line against multiple patterns, and if, let's
> > say, 4 patterns match, 4 lines will be output:
> >
> > 	smartctl <params> | awk '
> > 		/pattern1/
> > 		/pattern2/
> > 		/pattern3/
> > 		/pattern4/
> > 	' > out.txt
> >
> > If no action is provided, the whole line will be printed; if you
> > just want some (maybe postprocessed) fields of a line, add
> >
> > 	{ print <whatever }
> >
> > to each pattern. Another way is "counting down" the amount of
> > additional lines after one pattern has been found:
> >
> > 	smartctl <params> | awk '
> > 		{
> > 			if (nextlines > 0) {
> > 			        print;
> > 				nextlines--;
> > 			}
> > 		}
> > 		/pattern/ {
> > 			nextlines = 4;
> > 		}
> > 	' > out.txt
> >
> > The first block without a pattern will always be executed
> > (in this case, "print" is the command that will be called
> > for all desired lines), and the one with a pattern that
> > will "trigger" that first block to actually output something.
> >
> >
> >
> > By the way: If there is no processing, and you just need some
> > data lines as is, why not use grep?
> >
> > 	smartctl <params> | grep "<pattern>" -A 4 > out.txt
> >
> > See "man grep" for details on the -A option.
> 
> 
> Good point, here is the relevant part of the smartctl output:
> 
> [root at kabini1, /etc, 7:10:34pm] 915 % smartctl -l scttemp /dev/ada0
> smartctl 6.4 2015-06-04 r4109 [FreeBSD 9.3-RELEASE-p24 amd64] (local build)
> Copyright (C) 2002-15, Bruce Allen, Christian Franke, www.smartmontools.org
> 
> === START OF READ SMART DATA SECTION ===
> SCT Status Version:                  3
> SCT Version (vendor specific):       256 (0x0100)
> SCT Support Level:                   1
> Device State:                        Active (0)
> Current Temperature:                    27 Celsius
> Power Cycle Min/Max Temperature:     23/31 Celsius
> Lifetime    Min/Max Temperature:     19/33 Celsius
> Lifetime    Average Temperature:        23 Celsius
> Under/Over Temperature Limit Count:   0/0
> 
> I am invoking smartctl 4 times per drive to extract & process (print in 
> my own format) the 4 lines beginning w/ 'Current Temperature:'. I want 
> the resulting output in 1 line per drive as such (for 4 drives):
> 
> [root at kabini1, /etc, 7:32:53pm] 456 % hddtemp /dev/ada[0-3]
> SMART supported, SMART enabled
> drive /dev/ada0: HGST HTS721010A9E630, S/N: JR10006PGYH08E, Temp. 27 
> degC, min/max, cycle: 23/31, lifetime: 19/33, lifetime avg. 23 degC
> drive /dev/ada1: HGST HTS721010A9E630, S/N: JR10006PGYD6ZE, Temp. 28 
> degC, min/max, cycle: 24/33, lifetime: 20/33, lifetime avg. 24 degC
> drive /dev/ada2: HGST HTS721010A9E630, S/N: JR10006PGYTR8E, Temp. 27 
> degC, min/max, cycle: 24/33, lifetime: 19/33, lifetime avg. 24 degC
> drive /dev/ada3: HGST HTS721010A9E630, S/N: JR10006PGYK1YE, Temp. 27 
> degC, min/max, cycle: 23/31, lifetime: 19/33, lifetime avg. 23 degC
> [root at kabini1, /etc, 7:32:58pm] 457 %

Okay, I think I get the idea. How about this?

#!/bin/sh
echo -n "drive $1: "
smartctl -a $1 | awk -F ":" '
	{
		gsub("Celsius", "degC", $0);
		gsub(":[ \t]*", ":", $0);

	}
	/Device Model/ {
		printf("%s ", $2);
	}
	/Serial Number/ {
		printf("S/N: %s, ", $2);
	}
	/Current Temperature/ {
		printf("Temp: %s, ", $2);
	}
	/Power Cycle Min\/Max Temperature/ {
		printf("min/max, cycle: %s, ", $2);
	}
	/Lifetime    Min\/Max Temperature/ {
		printf("lifetime: %s, ", $2);
	}
	/Lifetime    Average Temperature/ {
		printf("lifetime avg: %s\n", $2);
	}
'

For "good style", make a [ -f for $1 before continuing,
and add a usage information message if $1 is not given.
Also point your finger at my gsub() and "space collapsing"
magic and say: "What a stupid way to do it!", because
I can imagine that there's a much better way to do it.
And note the "C contaminated" use of awk. :-)



> Funny you mention grep, I had a similar conversation on the NetBSD list 
> last week & everyone there suggested using awk alone to 'grep' out the 
> lines I wanted.

The idea of awk is that you intend to postprocess the text,
not just printing it 1:1 (what grep would do). In such a case,
using awk's builtin pattern matching is the way to go, because
awk is a "pattern-directed scanning and processing language",
according to its manpage.




> However, over there, the temp output I wanted was all in 
> 1 line, just not formatted as I wanted it.

That would have been a case for grep.





-- 
Polytropon
Magdeburg, Germany
Happy FreeBSD user since 4.0
Andra moi ennepe, Mousa, ...


More information about the freebsd-questions mailing list