Bash conditional expression: `-o`. What does it mean? - linux

I am trying to understand what dose the -o mean in the following bash if script.
looking at the results I can guess what it does but I do really need to get the concept of it.
i=1
for day in Mon Tue Wed Thu Fri Sat Sun
do
echo -n "Day $((i++)) : $day"
if [ $i -eq 7 -o $i -eq 8 ]; then
echo " (WEEKEND)"
continue;
fi
echo " (weekday)"
done
The results are as following:
$ ./for7.sh
Day 1 : Mon (weekday)
Day 2 : Tue (weekday)
Day 3 : Wed (weekday)
Day 4 : Thu (weekday)
Day 5 : Fri (weekday)
Day 6 : Sat (WEEKEND)
Day 7 : Sun (WEEKEND)

The -o symbolizes Logical OR here.
Do man test which explains this.
EXPRESSION1 -o EXPRESSION2
either EXPRESSION1 or EXPRESSION2 is true

Related

Parsing cal output in POSIX compliant shell script by read command

I am trying to write a POSIX compliant script, which will print all months in specified year $3, that have day in $1 (for example Mo, Tu,...) on a same date as $2 (1,2,3,...).
Example:
Input: ./task1.sh Tu 5 2006
Output:
September 2006
Mo Tu We Th Fr Sa Su
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30
December 2006
Mo Tu We Th Fr Sa Su
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
I have written this script:
#!/bin/sh
year=$3
dayInMonth=$2
dayInWeek=$1
index=1
while expr $index '!=' 13 >/dev/null; do
cal -m $index $year| tail -n +2| while read Mo Tu We Th Fr Sa Su ; do
eval theDay='$'$dayInWeek
if [ "$theDay" = "$dayInMonth" ]; then
cal -m $index $year;
fi
done
index=$(expr $index + 1)
done
But there is a problem with reading of third line of cal output. In these lines numbers of days usually don't start at Mo place. How can I parse third line of cal output so the numbers in $Mo, $Tu, $We,... are always correct?
Update: You've added the requirement for a posix conform solution. date -d as used in my answer is not POSIX conform. I'll keep the answer for those who are using GNU/Linux.
Btw, the following command gives you posixly correct the day of week offset of Jan 5, 2006:
cal 01 2006 | awk -v d=5 'NR>2{for(i=1;i<NF;i++){if($i==d){print i;exit}}}'
You need to tinker a little shell script around that.
I would use the date command, like this:
#!/bin/bash
dayofweek="${1}"
day="${2}"
year="${3}"
for m in {01..12} ; do
date=$(LANG=C date -d "${year}-${m}-${day}" +'%a %B')
read d m <<< "${date}"
[ "${d}" = "${dayofweek}" ] && echo "${m}"
done
Results:
$ bash script.sh Thu 05 2006
January
October
It's easier to check dates with the command date.
for month in {1..12}; do
if [[ $(date -d $(printf "%s-%2.2d-%2.2d" "$year" "$month" "$day") "+%a") == "Tue" ]]; then
cal -m $month $year;
fi
done
The script loops over the 12 months and generate a date based on year and day. The date command outputs the day of the in a 3 letters format with +%a.
If you want the day of week in number format, use +%u and == 2 in the if statement.

Get the next time occurance with linux date

Linux date utility can understand a lot of strings including for instance:
$ date -d '8:30'
Fri Jan 2 08:30:00 CET 2015
I'm looking for a way to get the next 8:30, thus:
in case it is Fri Jan 2 before 8:30, the result above should be returned;
otherwise it should print Sat Jan 3 08:30:00 CET 2015.
As one can see next 8:30 doesn't result in the correct answer:
$ date -d 'next 8:30'
date: invalid date ‘next 8:30’
Is there a single expression to calculate this?
Handling it in the shell oneself is of course an option, but makes things more complicates because of daylight save time regulation etc.
In case the clock is adapted to daylight save time, next 8:30 should be parsed to 8:30 according to the settings of the next day.
Testcase:
Given it is Fri Jan 2 12:01:01 CET 2015, the result should be:
$ date -d 'next 8:30'
Sat Jan 3 08:30:00 CET 2015
$ date -d 'next 15:30'
Fri Jan 2 15:30:00 CET 2015
Just use something like:
if [[ $(date -d '8:30 today' +%s) -lt $(date +%s) ]] ; then
next830="$(date -d '8:30 tomorrow')"
else
next830="$(date -d '8:30 today')"
fi
The %s format string gives you seconds since the epoch so the if statement is basically:
if 8:30-today is before now:
use 8:30-tomorrow
else
use 8:30-today
I researched and it does not seem to be possible to do so.
What you can probably do is to compare the hour and minute with 830 and print accordingly:
[ $(date '+%H%M') -le 830 ] && date -d '8:30' || date -d '8:30 + 1 day'
In case you want to work with this easily, create a function to do these calculations.
Test
$ [ $(date '+%H%M') -le 830 ] && date '8:30' || date -d '8:30 + 1 day'
Sat Jan 3 08:30:00 CET 2015

Can I grep for results compared to a timestamp?

I have a tab-delimited text file with three fields: TIMESTAMP, HOST, and STATUS. I need to find if a host was listed as down less than an hour ago. So far, I have this example:
grep "Down" thetextfile.txt | grep "thehostname"
That gives me a little list of all the times that a host was down in the log. Cool. Now I think I just need to get whether the latest TIMESTAMP is less than an hour ago. I am pretty new to Linux and Bash scripting, but in my other work with actual databases, this would be a relatively simple query.
Any ideas? Or is there a much better approach?
Here's an example of the log file:
TIMESTAMP HOST STATUS
Wed Oct 8 12:16:23 EDT 2014 aserver Alive
Wed Oct 8 12:16:23 EDT 2014 anotherserver Down
Thanks!
You can use this BASH script:
#!/bin/bash
# current date-time in seconds (epoch) value
now=$(date '+%s')
while read -r p; do
# ignore 1st row with headers
[[ "$p" == *TIMESTAMP* ]] && continue
# read 3 values in 3 variables t h s
IFS=$'\t' && read t h s <<< "$p"
# convert date string to epoch value
ts=$(date -d "$t" '+%s')
# if date from file is less than 1 hour ago and status is Down then print host name
[[ "$s" == "Down" ]] && (( (now-ts) < 3600 )) && echo "$h"
done < file
I'd use GNU awk:
gawk -v status=Down -v host=anotherserver '
BEGIN {
mo["Jan"]=1; mo["May"]=5; mo["Sep"]=9
mo["Feb"]=2; mo["Jun"]=6; mo["Oct"]=10
mo["Mar"]=3; mo["Jul"]=7; mo["Nov"]=11
mo["Apr"]=4; mo["Aug"]=8; mo["Dec"]=12
}
function elapsed(month, day, time, year) {
gsub(/:/, " ", time)
return systime() - mktime(sprintf("%d %02d %02d %s", year, mo[month], day, time));
}
$NF == status && $(NF-1) == host && elapsed($2,$3,$4,$6) < 3600
' <<DATA
TIMESTAMP HOST STATUS
Wed Oct 8 12:16:23 EDT 2014 aserver Alive
Wed Oct 8 12:16:23 EDT 2014 anotherserver Down
Wed Oct 16 10:16:23 EDT 2014 aserver Alive
Wed Oct 16 10:16:23 EDT 2014 anotherserver Down
Wed Oct 16 10:16:23 EDT 2014 aserver Down
Wed Oct 16 10:16:23 EDT 2014 anotherserver Up
DATA
Wed Oct 16 10:16:23 EDT 2014 anotherserver Down
Current date is Thu Oct 16 10:53:45 EDT 2014

How to add timestamp while redirecting stdout to file in Bash?

I have a program (server) and I am looking for a way (script) that will redirect (or better duplicate) all its stdout to file and add timestamp for each entry.
I've done some research and the furthest I could get was thanks to How to add timestamp to STDERR redirection. It redirects stdout but the timestamp added is of the time when the script finishes:
#!/bin/bash
./server | ./predate.sh > log.txt
code of predate.sh:
#!/bin/bash
while read line ; do
echo "$(date): ${line}"
done
It seems that server output is flushed after exit of the program.(without redirecting it works fine). Also if I try using predate.sh on given example in mentioned thread, it works perfectly. I am aware it would be easy adding a timestamp to the main program but I would rather avoid editing its code.
I recently needed exactly that: receive log messages in a serial console (picocom), print them to a terminal and to a file AND prepend the date.
What I now use looks s.th. like this:
picocom -b 115200 /dev/tty.usbserial-1a122C | awk '{ print strftime("%s: "), $0; fflush(); }' | tee serial.txt
the output of picocom is piped to awk
awk prepends the date (the %s option converts the time to the Number of seconds since 1970-01-01 00:00:00 UTC - or use %c for a human-readable format)
fflush() flushes any buffered output in awk
that is piped to tee which diverts it to a file. (you can find some stuff about tee here)
moreutils ts
Absolute date and time is the default:
$ sudo apt-get install moreutils
$ (echo a;sleep 1;echo b;sleep 3;echo c;sleep 2;echo d;sleep 1) | ts | tee myfile
$ cat myfile
Apr 13 03:10:44 a
Apr 13 03:10:45 b
Apr 13 03:10:48 c
Apr 13 03:10:50 d
or counting from program start with ts -s:
$ (echo a; sleep 1; echo b; sleep 3; echo c; sleep 2; echo d; sleep 1) | ts -s
00:00:00 a
00:00:01 b
00:00:04 c
00:00:06 d
or deltas for benchmarking with ts -i:
$ (echo a; sleep 1; echo b; sleep 3; echo c; sleep 2; echo d; sleep 1) | ts -i
00:00:00 a
00:00:01 b
00:00:03 c
00:00:02 d
$ (echo a; sleep 1; echo b; sleep 3; echo c; sleep 2; echo d; sleep 1) | ts -i '%.s'
0.000010 a
0.983308 b
3.001129 c
2.001120 d
See also: How to monitor for how much time each line of stdout was the last output line in Bash for benchmarking?
Tested on Ubuntu 18.04, moreutils 0.60.
For Me Your Code is working perfectly fine
Check this is what I tried
test.sh
#!/bin/bash
while true; do
echo "hello"
done
predate.sh
#!/bin/bash
while read line; do
echo $(date) ":" $line;
done
then
./test.sh | ./predate.sh
gives me
Tue Jan 14 17:49:47 IST 2014 : hello
Tue Jan 14 17:49:47 IST 2014 : hello
Tue Jan 14 17:49:47 IST 2014 : hello
Tue Jan 14 17:49:47 IST 2014 : hello
Tue Jan 14 17:49:47 IST 2014 : hello
Tue Jan 14 17:49:47 IST 2014 : hello
Tue Jan 14 17:49:47 IST 2014 : hello
Tue Jan 14 17:49:47 IST 2014 : hello
Tue Jan 14 17:49:47 IST 2014 : hello
Tue Jan 14 17:49:47 IST 2014 : hello
Tue Jan 14 17:49:47 IST 2014 : hello
Tue Jan 14 17:49:47 IST 2014 : hello
Tue Jan 14 17:49:47 IST 2014 : hello
Tue Jan 14 17:49:47 IST 2014 : hello
Tue Jan 14 17:49:47 IST 2014 : hello
This can be redirected to some file using ">" or ">>" for append
Again, using ts from moreutils, you can just use exec at the top of your script.
#!/bin/bash
exec > >(ts>>file.log)
echo hello 1
echo hello 2
sleep 5
echo hello 3
If I understand your problem is to have stderr output included in your log.txt file. Right ?
If that's what you want the solution is:
./server 2>&1 | ./predate.sh > log.txt
Regards

How to make date command work in relation to custom specified date?

In Linux there is pretty awesome command date which can be used is ways like this:
# Get some cool date in relation to systems date:
date -d "last Sunday -7 days"
Sun Sep 15 00:00:00 PDT 2013
# Set systems date:
date --set="2013-03-04"
Mon Mar 4 00:00:00 PST 2013
Basically I want to be able to run this command like this:
date --date="last Sunday -7 days" +%Y-%m-%d
2013-09-15
But not in relation to today's system date but in relation to some date generated by another computation in the form of string (e.g. "2013-09-01") or something else.
Please help me to figure out how to do that.
Using a function:
function get_last_day {
local date=$1 day=$2 format=$3 a b i
for (( i = 0; i <= 6; ++i )); do
read -r a b < <(exec date -d "$date - $i days" "+%a $format")
if [[ $a == "$day" ]]; then
echo "$b"
return
fi
done
}
get_last_day '2013-09-18' Sun '%Y-%m-%d'
Output:
2013-09-15

Resources