List all the mondays of this month - bash

I'm pretty new to bash and all the terminal in general - I've been messing around with cal and the date scripts wondering if there is anyway to list all the dates of monday of the current month .
My thought process is going thru the cal command, listing out the dates and maybe cutting a column from that input. Is that possible ?

You can do it with date command. Print 10 mondays since month ago:
for x in $(seq 0 9)
do
date -d "$x monday 5 week ago"
done
And grep only current month. Full command: for x in $(seq 0 9); do; date -d "$x monday 5 week ago"; done | grep $(date +%b)
Output:
Mon Jun 5 00:00:00 MSK 2017
Mon Jun 12 00:00:00 MSK 2017
Mon Jun 19 00:00:00 MSK 2017
Mon Jun 26 00:00:00 MSK 2017

Given:
$ cal
June 2017
Su Mo Tu We Th Fr Sa
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
You can do:
$ cal | awk 'NF>5{print $2}'
Mo
5
12
19
26
If you want something that will support any day of cal, use the field width (gawk only this):
$ cal | gawk -v n=5 '
BEGIN{
FIELDWIDTHS = "3 3 3 3 3 3 3"
}
FNR>1{print $n}'
Th
1
8
15
22
29
Or, as pointed out in comments:
$ ncal | awk '/^Mo/'
Mo 5 12 19 26

Combination of cal,cut commands to achieve the output.
cal -h| cut -c'4,5'
Remove the highlight and cut the characters which suits in the fields of monday.
ncal | sed -n '/^Mo/p'
The output as below:
Mo 5 12 19 26

Related

Get next month in terminal

In OSX on the terminal, how can cal be used to obtain the calendar of the next month?
i.e. the following
command: "cal -m && date +%m +1"
should equate to
command: "cal -m 7"
and produce:
July 2014
Su Mo Tu We Th Fr Sa
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
Use this:
m=$(date +'%m'); ((m++)) ; cal -m $m
or this:
cal $(date -v+1m +'%m %y')

UNIX SHELL Script Replace text with Sed,i have the program working,but for a simple particular case it is not working,how can i correct it?

Problem statement is
If todays date is single digit,then replace that date with single *
if the today date is double digit,then replace it with double *
my code works for single digit date.
but my sed code is unable to substitute double digit with double **.
Here is my code:
#!/bin/sh
set `date`
if [ $3 -le 9 ]
then
n=`cal | tail -n +3 | grep -n "$3"| cut -d ":" -f1 | head -n 1`
n=`expr $n + 2`
cal | sed "$n s/$3/*/"
else
cal | sed "s/$3/**/"
fi
whats the error in this line
cal | sed "s/$3/**/"
assume $3 contains value 19.
here's the output of cal | cat -vte
abhijith#abhijith-compaq-420:~/Desktop$ cal | cat -vte
December 2013 $
Su Mo Tu We Th Fr Sa $
1 2 3 4 5 6 7 $
8 9 10 _^H1_^H1 12 13 14 $
15 16 17 18 19 20 21 $
22 23 24 25 26 27 28 $
29 30 31 $
$
cal | sed "s/$3/**/" works fine for me. But I recommend to rewrite your script this way:
#!/bin/sh
day=`date +%d`
case $day in
?) cal | sed "s/ $day/ */" ;;
??) cal | sed "s/$day/**/" ;;
esac
UPDATE
Since you say it's not working for you, let's get back to the basics. What will be the output of this script:
#!/bin/sh
day=9; cal | sed "s/ $day/ */"
day=19; cal | sed "s/$day/**/"
I get:
December 2013
S M Tu W Th F S
1 2 3 4 5 6 7
8 * 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
December 2013
S M Tu W Th F S
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 ** 20 21
22 23 24 25 26 27 28
29 30 31

Script for counting weekends

Script for counting weekends (saturdays AND sundays) in a given month, by passing in month and year. How to write it?
New in scripting, can't find solution to this easy task.
You want to count weekends Sat and Sun, So I am guessing you mean 2 day weekends.
Hope this helps. VonBell
In unix/linux you can easily parse the cal command for the given month.
$ cal 04 2013
April 2013
Su Mo Tu We Th Fr Sa
... 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
The numerical date always start on Line 3, the cols for each day are constant
When I used the commands below to extract the weekend (sun, Sat)
As you can see Sun is cols 1,2,3 (with space included) and Sat is cols 19,20
$ cal 04 2013|tail +3|cut -c1-3,19,20
.. 6
7 13
14 20
21 27
28
When I execute the script below as cntWkEnd.sh The output is as follows
1 equals one day wkend and 2 equals 2 day wkend sat and sun
$ ./cntWkEnd.sh 04 2013
1
2
2
2
1
0
To count only 2 day weekends you can add at the command line or in the script
the following
./cntWkEnd.sh 04 2013|grep "2"|wc -l (Output of 3 shown below)
3
This the contents of cntWkEnd.sh
#!/bin/bash
cal $1 $2|tail +3|cut -c1-3,19,20 |\
while read WkEnd
do
echo $WkEnd|wc -w
done

unix cal command special character

When I try "cal | tail -6" in my unix machine, I get -
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
but when I try "cal | tail -6 | awk '{print $7}'", I get -
10
17
24
where is 3 going ? My requirement is basically all weekdays i.e column 2,3,4,5 & 6.
But I'm getting wrong output because of the strange behavior of "cal"
There are only 3 whitespace delimited columns in your first row. cal is working exactly as corrected, you are not understanding how awk works. As far as awk is concerned there is no 7th column in your first row as it yields attention to whitespace delimited columns, not fixed width columns.
A quick google search reveals you can use
BEGIN { FIELDWIDTHS = "3 3 3 3 3 3 3" }
In your awk script.
Since all of your columns in each row are three characters wide, you could use this to extract the days you wish for. For example, if you wanted only the 7th day in a column, you could do the following:
cal | sed 's/^\(.\{18\}\).*$/\1/'
This command would remove the first 18 characters in the line, which are the entries for the first 6 days of the week.
To extract a particular day, such as the fourth day, you could do this:
cal | sed 's/^.\{9\}\(.\{3\}\).*$/\1/'
To remove the first day of the week and the last day, you could do this:
cal | sed -e 's/^.\{3\}//' -e 's/^\(.\{15\}\).\{3\}$/\1/'
May be a row-wise extraction will do the trick. Try ncal. For example:
$ ncal
November 2012
Mo 5 12 19 26
Tu 6 13 20 27
We 7 14 21 28
Th 1 8 15 22 29
Fr 2 9 16 23 30
Sa 3 10 17 24
Su 4 11 18 25
or fill the absent dates with place holder (with '-' for example):
kent$ cal -s|tail -6|awk 'NR==1&&NF<7{gsub(/^ */,"");for(i=1;i<=(7-NF);i++) x=" - "x;$0=x" "$0;}1'
- - - - 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
then you could get the column, replace '-' with " " if needed. e.g. for $7:
kent$ cal -s|tail -6|awk 'NR==1&&NF<7{gsub(/^ */,"");for(i=1;i<=(7-NF);i++) x=" - "x;$0=x" "$0;}{print $7}'
3
10
17
24
Note that todays date is highlighted unless you turn it off (-h). Use cut to extract the wanted columns:
cal -h | cut -c19-20
Output:
Sa
3
10
17
24

How to use bash to get the last day of each month for the current year without using if else or switch or while loop?

As we know that each year have the following max day in each month as follows:
Jan - 31 days
Feb - 28 days / 29 days (leap year)
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days
How to I get bash to return the value (last day of each month) for the current year without using if else or switch or while loop?
my take:
for m in {1..12}; do
date -d "$m/1 + 1 month - 1 day" "+%b - %d days";
done
To explain: for the first iteration when m=1 the -d argument is "1/1 + 1 month - 1 day" and "1/1" is interpreted as Jan 1st. So Jan 1 + 1 month - 1 day is Jan 31. Next iteration "2/1" is Feb 1st, add a month subtract a day to get Feb 28 or 29. And so on.
cat <<EOF
Jan - 31 days
Feb - `date -d "yesterday 3/1" +"%d"` days
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days
EOF
cal $(date +"%m %Y") |
awk 'NF {DAYS = $NF}; END {print DAYS}'
This uses the standard cal utility to display the specified month, then runs a simple Awk script to pull out just the last day's number.
Assuming you allow "for", then the following in bash
for m in {1..12}; do
echo $(date -d $m/1/1 +%b) - $(date -d "$(($m%12+1))/1 - 1 days" +%d) days
done
produces this
Jan - 31 days
Feb - 29 days
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days
Note: I removed the need for cal
For those that enjoy trivia:
Number months from 1 to 12 and look at the binary representation in four
bits {b3,b2,b1,b0}. A month has 31 days if and only if b3 differs from b0.
All other months have 30 days except for February.
So with the exception of February this works:
for m in {1..12}; do
echo $(date -d $m/1/1 +%b) - $((30+($m>>3^$m&1))) days
done
Result:
Jan - 31 days
Feb - 30 days (wrong)
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days
Try using this code
date -d "-$(date +%d) days month" +%Y-%m-%d
Returns the number of days in the month compensating for February changes in leap years without looping or using an if statement
This code tests date to see if Feb 29th of the requested year is valid, if so then it updates the second character in the day offset string. The month argument selects the respective substring and adds the month difference to 28.
function daysin()
{
s="303232332323" # normal year
((!($2%4)&&($2%100||!($2%400)))) && s=313232332323 # leap year
echo $[ ${s:$[$1-1]:1} + 28 ]
}
daysin $1 $2 #daysin [1-12] [YYYY]
On a Mac which features BSD date you can just do:
for i in {2..12}; do date -v1d -v"$i"m -v-1d "+%d"; done
Quick Explanation
-v stands for adjust. We are adjusting the date to:
-v1d stands for first day of the month
-v"$i"m defined the month e.g. (-v2m for Feb)
-v-1d minus one day (so we're getting the last day of the previous month)
"+%d" print the day of the month
for i in {2..12}; do date -v1d -v"$i"m -v-1d "+%d"; done
31
28
31
30
31
30
31
31
30
31
30
You can add year of course. See examples in the manpage (link above).
Contents of script.sh:
#!/bin/bash
begin="-$(date +'%-m') + 2"
end="10+$begin"
for ((i=$begin; i<=$end; i++)); do
echo $(date -d "$i month -$(date +%d) days" | awk '{ printf "%s - %s days", $2, $3 }')
done
Results:
Jan - 31 days
Feb - 29 days
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
for m in $(seq 1 12); do cal $(date +"$m %Y") | grep -v "^$" |tail -1|grep -o "..$"; done
iterate from 1 to 12 (for...)
print calendar table for each month (cal...)
remove empty lines from output (grep -v...)
print last number in the table (tail...)
There is no sense, to avoid using cal, because it is required by POSIX, so should be there
A variation for the accepted answer to show the use of "yesterday"
$ for m in {1..12}; do date -d "yesterday $m/1 + 1 month" "+%b - %d days"; done
Jan - 31 days
Feb - 28 days
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days
How it works?
Show the date of yesterday for the date "month/1" after adding 1 month
I needed this few times, so when in PHP comes with easy in bash is not,
so I used this till throw me error "invalid arithemtic operator"
and even with warrings in spellcheck ( "mt" stands for month, "yr" for year )
last=$(echo $(cal ${mt} ${yr}) | awk '{print $NF}')
so this works fine...
### get last day of month
#
# implement from PHP
# src: https://www.php.net/manual/en/function.cal-days-in-month.php
#
if [ $mt -eq 2 ];then
if [[ $(bc <<< "${yr} % 4") -gt 0 ]];then
last=28
else
if [[ $(bc <<< "${yr} % 100") -gt 0 ]];then
last=29
else
[[ $(bc <<< "${yr} % 400") -gt 0 ]] && last=28 || last=29
fi
fi
else
[[ $(bc <<< "(${mt}-1) % 7 % 2") -gt 0 ]] && last=30 || last=31
fi
Building on patm's answer using BSD date for macOS (patm's answer left out December):
for i in {1..12}; do date -v1m -v1d -v+"$i"m -v-1d "+%b - %d days"; done
Explanation:
-v, when using BSD date, means adjust date to:
-v1m means go to first month (January of current year).
-v1d means go to first day (so now we are in January 1).
-v+"$i"m means go to next month.
-v-1d means subtract one day. This gets the last day of the previous month.
"+%b - %d days" is whatever format you want the output to be in.
This will output all the months of the current year and the number of days in each month. The output below is for the as-of-now current year 2022:
Jan - 31 days
Feb - 28 days
Mar - 31 days
Apr - 30 days
May - 31 days
Jun - 30 days
Jul - 31 days
Aug - 31 days
Sep - 30 days
Oct - 31 days
Nov - 30 days
Dec - 31 days

Resources