using awk and bash for monitoring exec output to log - linux

I am looking for some help with awk and bash commands,
my project have an embedded (so very limited) hardware,
i need to run a specific command called "digitalio show"
the command output is:
Input=0x50ff <-- last char only change
Output=0x7f
OR
Input=0x50fd <-- last char only change
Output=0x7f
i need to extract the input parameter and convert it into either Active or Passive and log them to a file with timestamp.
the log file should look like this:
YYMMDDhhmmss;Active
YYMMDDhhmmss;Passive
YYMMDDhhmmss;Active
YYMMDDhhmmss;Passive
while logging only changes
The command "digitalio show" is an embedded specific command that give the I/O state at the time of the execution, so i basically need to log every change in the I/O into a file using a minimal tools i have in the embedded H/W.
i can run the command for every 500msec, but if i will log all the outputs i can finish the flash very quickly, so i need only log changes.
in the end this will run as a background deamon.
Thanks !
Rotem.

As far as I understand, a single run of digitalio show command outputs two lines in the following format:
Input=HEX_NUMBER
Output=0x7f
where HEX_NUMBER is either 0x50ff, or 0x50fd. Suppose, the former stands for "Active", the latter for "Passive".
Running the command once per 500 milliseconds requires keeping the state. The most obvious implementation is a loop with a sleep.
However, sleep implementations vary. Some of them support a floating point argument (fractional seconds), and some don't. For example, the GNU implementation accepts arbitrary floating point numbers, but the standard UNIX implementation guarantees to suspend execution for at least the integral number of seconds. There are many alternatives, though. For instance, usleep from killproc accepts microseconds. Alternatively, you can write your own utility.
Let's pick the usleep command. Then the Bash script may look like the following:
#!/bin/bash -
last_state=
while true ; do
i=$(digitalio show | awk -F= '/Input=0x[a-zA-Z0-9]+/ {print $2}')
if test "$i" = "0x50ff" ; then
state="Active"
else
state="Passive"
fi
if test "$state" != "$last_state" ; then
printf '%s;%s\n' $(date '+%Y%m%d%H%M%S') "$state"
fi
last_state="$state"
usleep 500000
done
Sample output
20161019103534;Active
20161019103555;Passive
The script launches digitalio show command in an infinite loop, then extracts the hex part from Input lines with awk.
The $state variable is assigned to whether "Active", or "Passive" depending on the value of hex string.
The $last_state variable keeps the value of $state in the last iteration. If $state is not equal to $last_state, then the state is printed to the standard output in the specific format.

Related

Linux Read - Timeout after x seconds *idle*

I have a (bash) script on a server that I have inherited the administration aspect of, and have recently discovered a flaw in the script that nobody has brought to my attention.
After discovering the issue, others have told me that it has been irritating them, but never told me (great...)
So, the script follows this concept
#!/bin/bash
function refreshscreen(){
# This function refreshes a "statistics screen"
...
echo "Enter command to override update"
read -t 10 variable
}
This script refreshes a statistics screen, and allows the user to stall the update in lieu of commands built into a case statement. However, the read times-out (read -t 10) after 10 seconds, regardless of if the user is typing.
Long story short, is there a way to prevent read from timing out if the user is actively typing a command? Best case scenario would be a "Time out of SEC idle/inactive seconds" opposed to just timeout after x seconds.
I have thought about running a background script at the end of the cycle before the read command pauses the screen to check for inactivity, but have not found a way to make that command work.
You can use read in a loop, reading one character at a time, and adding it to a final read string. This would then give the user some timeout amount of time per character rather than per command. Here's a sample function you might be able to incorporate into your script that shows what I'm talking about:
read_with_idle_timeout() {
local input=""
read -t 10 -N 1 variable
while [ ! -z $variable ]
do
input+=$variable
read -t 10 -N 1 variable
done
echo "Read: $input"
}
This will give the user 10 seconds to type each character. If they stop typing, you'll get as much of the command as they had started typing before the timeout occurred, and then your case statement can handle it. Perhaps you can store the final string in a global variable, or just put this code directly into your other function.
If you need more than one word, since read breaks on $IFS, you could call this function multiple times until you get all the input you're expecting.
I have searched for a simple solution that will do the following:
timeout after 10 seconds, if there is no user input at all
the user has infinite time to finish his answer if the first character was typed within the first 10 sec.
This can be implemented in two lines as follows:
read -N 1 -t 10 -p "What is your name? > " a
[ "$a" != "" ] && read b && echo "Your name is $a$b" || echo "(timeout)"
In case the user waits 10 sec before he enters the first character, the output will be:
What is your name? > (timeout)
If the user types the first character within 10 sec, he has unlimited time to finish this task. The output will look like follows:
What is your name? > Oliver
Your name is Oliver
Caveat: the first character is not editable, once it was typed, while all other characters can be edited (backspace and re-type). Any ideas for a simple solution?

How to create a bash variable like $RANDOM

I'm interest in some thing : every time I echo $RANDOM , the show value difference . I guess the RANDOM is special (When I read it , it may call a function , set a variable flag and return the RANDOM number . ) . I want to create a variable like this , how I can do it ? Every answer will be helpful .
The special behavior of $RANDOM is a built-in feature of bash. There is no mechanism for defining your own special variables.
You can write a function that prints a different value each time it's called, and then invoke it as $(func). For example:
now() {
date +%s
}
echo $(now)
Or you can set $PROMPT_COMMAND to a command that updates a specified variable. It runs just before printing each prompt.
i=0
PROMPT_COMMAND='((i++))'
This doesn't work in a script (since no prompt is printed), and it imposes an overhead whether you refer to the variable or not.
If you are BASH scripting there is a $RANDOM variable already internal to BASH.
This post explains the random variable $RANDOM:
http://tldp.org/LDP/abs/html/randomvar.html
It generates a number from 0 - 32767.
If you want to do different things then something like this:
case $RANDOM in
[1-10000])
Message="All is quiet."
;;
[10001-20000])
Message="Start thinking about cleaning out some stuff. There's a partition that is $space % full."
;;
[20001-32627])
Message="Better hurry with that new disk... One partition is $space % full."
;;
esac
I stumbled on this question a while ago and wasn't satisfied by the accepted answer: He wanted to create a variable just like $RANDOM (a variable with a dynamic value), thus I've wondered if we can do it without modifying bash itself.
Variables like $RANDOM are defined internally by bash using the dynamic_value field of the struct variable. If we don't want to patch bash to add our custom "dynamic values" variables, we still have few other alternatives.
An obscure feature of bash is loadable builtins (shell builtins loaded at runtime), providing a convenient way to dynamically load new symbols via the enable function:
$ enable --help|grep '\-f'
enable: enable [-a] [-dnps] [-f filename] [name ...]
-f Load builtin NAME from shared object FILENAME
-d Remove a builtin loaded with -f
We now have to write a loadable builtin providing the functions (written in C) that we want use as dynamic_value for our variables, then setting the dynamic_value field of our variables with a pointer to the chosen functions.
The production-ready way of doing this is using an another loadable builtin crafted on purpose to do the heavy-lifting, but one may abuse gdb if the ptrace call is available to do the same.
I've made a little demo using gdb, answering "How to create a bash variable like $RANDOM?":
$ ./bashful RANDOM dynamic head -c 8 /dev/urandom > /dev/null
$ echo $RANDOM
L-{Sgf

Handle "race-condition" between 2 cron tasks. What is the best approach?

I have a cron task that runs periodically. This task depends on a condition to be valid in order to complete its processing. In case it matters this condition is just a SELECT for specific records in the database. If the condition is not satisfied (i.e the SELECT does not return the result set expected) then the script exits immediately.
This is bad as the condition would be valid soon enough (don't know how soon but it will be valid due to the run of another script).
So I would like somehow to make the script more robust. I thought of 2 solutions:
Put a while loop and sleep constantly until the condition is
valid. This should work but it has the downside that once the script
is in the loop, it is out of control. So I though to additionally
after waking up to check is a specific file exists. If it does it
"understands" that the user wants to "force" stop it.
Once the script figures out that the condition is not valid yet it
appends a script in crontab and stops. That seconds script
continually polls for the condition and if the condition is valid
then restart the first script to restart its processing. This solution to me it seems to work but I am not sure if it is a good solution. E.g. perhaps programatically modifying the crontab is a bad idea?
Anyway, I thought that perhaps this problem is common and could have a standard solution, much better than the 2 I came up with. Does anyone have a better proposal? Which from my ideas would be best? I am not very experienced with cron tasks so there could be things/problems I could be overseeing.
instead of programmatically appending the crontab, you might want to consider using at to schedule the job to run again at some time in the future. If the script determines that it cannot do its job now, it can simply schedule itself to run again a few minutes (or a few hours, as it may) later by way of an at command.
Following up from our conversation in comments, you can take advantage of conditional execution in a cron entry. Supposing you want to branch based on time of day, you might use the output from date.
For example: this would always invoke the first command, then invoke the second command only if the clock hour is currently 11:
echo 'ScriptA running' ; [ $(date +%H) == 11 ] && echo 'ScriptB running'
More examples!
To check the return value from the first command:
echo 'ScriptA' ; [ $? == 0 ] echo 'ScriptB'
To instead check the STDOUT, you can use as colon as a noop and branch by capturing output with the same $() construct we used with date:
: ; [ $(echo 'ScriptA') == 'ScriptA' ] && echo 'ScriptB'
One downside on the last example: STDOUT from the first command won't be printed to the console. You could capture it to a variable which you echo out, or write it to a file with tee, if that's important.

Calculating the difference between the first words(timestamp) using perl dynamically

I have a program that keeps on writing the icmp echo requests being received by a machine into a file.
I am using system ("tcpdump icmpecho[0] == 8 | tee abc.txt") to do that.
So this process keeps on going till I end the program manually.
Each line has the timestamp as its first word.
now i want to calculate the frequency of the echo requests I am receiving using a separate script so that if it reaches a certain threshold , I can print an alert.
I tried to use grep -Eo '^[^ ]+' file
to get the timestamps into an array, but I dont know what to do after getting them into an array. grep goes on in a while loop since the file it is reading from keeps on getting populated infinitely.(I'll not have an option of monitoring the differences and printing an alert if grep goes on like that right?)
All I am trying to do is to keep track of the frequency of icmp echo requests that are coming in on my machine and print an alert message whenever that frequency crosses a threshold. is there any alternative way?
All timestamps are saved in #arr
perl -ne '$f{$_}++ or push #arr, $_ for /(\d+:\d+)/ }{ print "$_ [$f{$_} times]\n" for #arr' file
constantly reading from log file,
perl -e 'open$T,pop;while(1){while(<$T>){ ++$f{$_}>10 and print "[$f{$_}]$_" for /(\d+:\d+)/ }sleep 1;seek $T,0,1}' file
I am using
tcpstat -i eth1 -f icmp[0] == 8
to get the request count. it gives me 3 more parameters but got to research a bit bout them!

Pipe output to bash function

I have as simple function in a bash script and I would like to pipe stdout to it as an input.
jc_hms(){
printf "$1"
}
I'd like to use it in this manner.
var=`echo "teststring" | jc_hms`
Of course I used redundant functions echo and printf to simplify the question, but you get the idea. Right now I get a "not found" error, which I assume means my parameter delimiting is wrong (the "$1" part). Any suggestions?
Originally the jc_hms function was used like this:
echo `jc_hms "teststring"` > //dev/tts/0
but I'd like to store the results in a variable for further processing first, before sending it to the serial port.
EDIT:
So to clarify, I am NOT trying to print stuff to the serial port, I'd like to interface to my bash functions should the "|" pipe character, and I am wondering if this is possible.
EDIT: Alright, here's the full function.
jc_hms(){
hr=$(($1 / 3600))
min=$(($1 / 60))
sec=$(($1 % 60))
printf "$hs:%02d:%02d" $min $sec
}
I'm using the function to form a string which come this line of code
songplaytime=`echo $songtime | awk '{print S1 }'`
printstring="`jc_hms $songplaytime`" #store resulting string in printstring
Where $songtime is a string expressed as "playtime totaltime" delimited by a space.
I wish I can just do this in one line, and pipe it after the awk
printstring=`echo $songtime | awk '{print S1 }' | jc_hms`
like so.
To answer your actual question, when a shell function is on the receiving end of a pipe, standard input is inherited by all commands in the function, but only commands that actually read form their standard input consume any data. For commands that run one after the other, later commands can only see what isn't consumed by previous commands. When two commands run in parallel, which commands see which data depends on how the OS schedules the commands.
Since printf is the first and only command in your function, standard input is effectively ignored. There are several ways around that, including using the read built-in to read standard input into a variable which can be passed to printf:
jc_hms () {
read foo
hr=$(($foo / 3600))
min=$(($foo / 60))
sec=$(($foo % 60))
printf "%d:%02d:%02d" "$hr" "$min" "$sec"
}
However, since your need for a pipeline seems to depend on your perceived need to use awk, let me suggest the following alternative:
printstring=$( jc_hms $songtime )
Since songtime consists of a space-separated pair of numbers, the shell performs word-splitting on the value of songtime, and jc_hms sees two separate parameters. This requires no change in the definition of jc_hms, and no need to pipe anything into it via standard input.
If you still have a different reason for jc_hms to read standard input, please let us know.
You can't pipe stuff directly to a bash function like that, however you can use read to pull it in instead:
jc_hms() {
while read -r data; do
printf "%s" "$data"
done
}
should be what you want
1) I know this is a pretty old post
2) I like most of the answers here
However, I found this post because I needed to something similar. While everyone agrees stdin is what needs to be used, what the answers here are missing is the actual usage of the /dev/stdin file.
Using the read builtin forces this function to be used with piped input, so it can no longer be used in a typical way. I think utilizing /dev/stdin is a superior way of solving this problem, so I wanted to add my 2 cents for completeness.
My solution:
jc_hms() {
declare -i i=${1:-$(</dev/stdin)};
declare hr=$(($i/3600)) min=$(($i/60%60)) sec=$(($i%60));
printf "%02d:%02d:%02d\n" $hr $min $sec;
}
In action:
user#hostname:pwd$ jc_hms 7800
02:10:00
user#hostname:pwd$ echo 7800 | jc_hms
02:10:00
I hope this may help someone.
Happy hacking!
Or, you can also do it in a simple way.
jc_hms() {
cat
}
Though all answers so far have disregarded the fact that this was not what OP wanted (he stated the function is simplified)
I like user.friendly's answer using the Bash built-in conditional unset substitution syntax.
Here's a slight tweak to make his answer more generic, such as for cases with an indeterminate parameter count:
function myfunc() {
declare MY_INPUT=${*:-$(</dev/stdin)}
for PARAM in $MY_INPUT; do
# do what needs to be done on each input value
done
}
Hmmmm....
songplaytime=`echo $songtime | awk '{print S1 }'`
printstring="`jc_hms $songplaytime`" #store resulting string in printstring
if you're calling awk anyway, why not use it?
printstring=`TZ=UTC gawk -vT=$songplaytime 'BEGIN{print strftime("%T",T)}'`
I'm assuming you're using Gnu's Awk, which is the best one and also free; this will work in common linux distros which aren't necessarily using the most recent gawk. The most recent versions of gawk will let you specify UTC as a third parameter to the strftime() function.
The proposed solutions require content on stdin or read to be only conditionally called. Otherwise the function will wait for content from the console and require an Enter or Ctrl+D before continuing.
A workaround is to use read with a timeout. e.g. read -t <seconds>
function test ()
{
# ...
# process any parameters
# ...
read -t 0.001 piped
if [[ "${piped:-}" ]]; then
echo $piped
fi
}
Note, -t 0 did not work for me.
You might have to use a different value for the time-out.
Too small a value might result in bugs and a too large time-out delays the script.
seems nothing works, but there are work arounds
mentioned work around xargs ref function
$ FUNCS=$(functions hi); seq 3 | xargs -I{} zsh -c "eval $FUNCS; hi {}"
then this doesn't work either because your function could reference another function. so I ended up writing some function that accepts pipe inputs, like this:
somefunc() {
while read -r data; do
printf "%s" "$data"
done
}

Resources