Hello again,
I wrote a small looping AWK script to practice use of functions and have
a few questions which maybe some of you could weigh in on.
The script:
--
#! /usr/bin/awk -f
# dwmstat.awk -- populate dwm(1) window mgr status area.
BEGIN {
while(1) {
status_str = " " temp() " | " load() " | " date() " "
#system("xsetroot -name '"status_str"'")
printf "%s\n", status_str
sleep()
}
}
function temp() {
while ("sensors -A coretemp-isa-0000"|getline) {
if ($0 ~ /Package/) {
sub("\\+","",$4)
TEMP = "core: " $4
break
}
}
close("sensors -A coretemp-isa-0000")
return TEMP
}
function uptime() {
"uptime -p" |getline
close("uptime -p")
sub("up","&:")
sub("ou","")
sub("utes","")
return $0
}
function load() {
"uptime" |getline
close("uptime")
sub("^.*age:","load:")
return $0
}
function date() {
"date '+%a %b %d %Y | %I:%M.%S %p'" |getline
close("date '+%a %b %d %Y | %I:%M.%S %p'")
sub("\n","")
return $0
}
function sleep () {
return system("sleep 5");\
close("sleep 5")
}
--
The script returns a status line that looks like this:
core: 33.0°C | load: 0.05, 0.10, 0.09 | Mon Feb 20 2023 | 10:44.49 AM
The commented out xsetroot(1) line will eventually be used to write
status area via the "-name" parameter; 'printf "%s\n", status_str' is
just for testing.
The questions:
All the functions just return "$0" and the script as written appears to
run fine. However I've also written a version that uses VARS in the
various functions, i.e.
--
function load_v() {
"uptime" |getline LOAD
close("uptime")
sub("^.*age:","load:",LOAD)
return LOAD
}
--
I did this because I noticed that while omitting the function vars
mostly works in some cases -- splitting date and time into two functions
for example -- the 'return $0' is contaminated with data returned from
other functions. Is this because "$0" is ultimately a global variable
or something else, say, a lack of garbage collection?
In case it matters:
- OS system being used: Debian 11.x
- AWKs being used: gawk and mawk
Other questions:
- should 'load_v()' be 'load( LOAD)' ? Why?
- are all these close() calls really necessary?
- can this script be improved or streamlined further?
Regards,
jeorge
Thanks for the feedback. Ya, embedding shell commands in AWK frame was just a practice thing mostly; probably not much of an additional burden
on most modern computers.
Embedding shell commands in AWK introduces a massive burden on any
computer, often turning tasks that should run in seconds or minutes into tasks that take hours or days to run, due to awk having to create a
subshell each time it has to call such a command. Consider this with
just 1000 lines of input:
1) Call a GNU awk function to print the seconds since the epoch:
$ time seq 1000 | awk '{print systime()}' >/dev/null
real 0m0.040s
user 0m0.000s
sys 0m0.000s
2) Embed a shell command to do the same thing:
$ time seq 1000 | awk '{system("date +%s")}' >/dev/null
real 0m29.628s
user 0m0.420s
sys 0m2.410s
3) Doing the same thing in a shell loop (slow but still much faster than calling it from awk):
$ time { seq 1000 | while IFS= read -r; do date +%s; done >/dev/null; }
real 0m17.796s
user 0m0.858s
sys 0m2.198s
On 2/26/23 2:43 PM, Ed Morton wrote:
Embedding shell commands in AWK introduces a massive burden on any
computer, often turning tasks that should run in seconds or minutes
into tasks that take hours or days to run, due to awk having to create
a subshell each time it has to call such a command. Consider this with
just 1000 lines of input:
1) Call a GNU awk function to print the seconds since the epoch:
$ time seq 1000 | awk '{print systime()}' >/dev/null
real 0m0.040s
user 0m0.000s
sys 0m0.000s
2) Embed a shell command to do the same thing:
$ time seq 1000 | awk '{system("date +%s")}' >/dev/null
real 0m29.628s
user 0m0.420s
sys 0m2.410s
3) Doing the same thing in a shell loop (slow but still much faster
than calling it from awk):
$ time { seq 1000 | while IFS= read -r; do date +%s; done >/dev/null; }
real 0m17.796s
user 0m0.858s
sys 0m2.198s
Hmm, I guess my computer is a bit faster:
$ time seq 1000 | awk '{print systime()}' >/dev/null
real 0m0.004s
user 0m0.003s
sys 0m0.002s
$ time seq 1000 | awk '{system("date +%s")}' >/dev/null
real 0m0.836s
user 0m0.782s
sys 0m0.099s
$ time { seq 1000 | while IFS= read -r; do date +%s; done >/dev/null; }
real 0m0.826s
user 0m0.676s
sys 0m0.218s
But I do take your point -- using systime() is over 200 times faster. Probably one simple uses something other than awk when things need to
happen quickly/efficiently and awk lacks a built-in.
Looking at my practice script some of the data could be pulled from
/proc, i.e. load and uptime. Other things like core temp, pulled from sensors(1), or battery charge, pulled from upower(1), might not be too
bad if only done every minute or so.
Anyway, I appreciate the feedback. I should probably try to rein in my compulsion to over-apply awk as I learn more about it.
jeorge
Embedding shell commands in AWK introduces a burden on small
computers, often turning tasks that should run in micro-seconds into
tasks that take seconds or minutes to run, due to awk having to create a
subshell each time it has to call such a command.
Looking at my practice script some of the data could be pulled from
/proc, i.e. load and uptime. Other things like core temp, pulled from >sensors(1), or battery charge, pulled from upower(1), might not be too
bad if only done every minute or so.
In article <tth6o8$2o3p$1@nnrp.usenet.blueworldhosting.com>,
jeorge <someone@invalid.invalid> wrote:
[...]
Given what I think I understand about your task(s), it'd probably be better to just write it as a (bash) shell script.
Modern bash has most of what
you need to do real scripting. About the only thing missing is decimal
(aka, floating point) arithmetic, and this can usually be easily done via a "bc" co-process (or, in a pinch, a call to awk).
[...]
On 27.02.2023 08:26, Kenny McCormack wrote:
In article <tth6o8$2o3p$1...@nnrp.usenet.blueworldhosting.com>,
jeorge <som...@invalid.invalid> wrote:
[...]
Given what I think I understand about your task(s), it'd probably be betterYeah, that's what I also thought when I upthread asked for a rationale
to just write it as a (bash) shell script.
of using awk as technical frame for a shell task. - I read the OP's statements as if he's just experimenting with awk.
Modern bash has most of whatNote that the shell features are minimalistic here, so any standard
you need to do real scripting. About the only thing missing is decimal (aka, floating point) arithmetic, and this can usually be easily done via a
"bc" co-process (or, in a pinch, a call to awk).
POSIX shell will do, and then you can use ksh (instead of bash) to
also do the FP arithmetics in shell and avoid clumsy and inefficient workaround with external processes.
Janis
if you only care for unix epochs numerically, and don't mind constantly resetting your rand() seed, then here's one way to extract it within just about any awk, even those without systime() :[…]
Sysop: | DaiTengu |
---|---|
Location: | Appleton, WI |
Users: | 793 |
Nodes: | 10 (1 / 9) |
Uptime: | 38:00:33 |
Calls: | 11,106 |
Calls today: | 3 |
Files: | 186,086 |
Messages: | 1,751,440 |