0

I'm trying to pick off the first Monday, second Tuesday, second Saturday, etc. out of the cal function and piping awk commands. For this month, I can get the first Tuesday correctly with the command below the calendar:

    December 2018
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

>cal | awk 'NF == 7 && NR > 2 {print $3; exit}'
>4

But this doesn't always work, see the test for March 2016 below:

     March 2016
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

>cal 3 2016 | awk 'NF == 7 && NR > 2 {print $3; exit}'
>8

Why does it work for the top example but not the bottom? I would think the bottom example should look at the third line and find the correct result of 1. Is there a fool-proof way to snag these dates using awk -- maybe checking if the column is empty and moving to the next line?

  • The third row doesn't have 7 fields – Jeff Schaller Dec 7 '18 at 19:44
  • Do you have to use cal? Do you have GNU date? – Jeff Schaller Dec 7 '18 at 19:51
  • I see about the NF==7 part, but removing that gets hairy if Sunday and Monday are missing and the first day of the month is Tuesday. I do have date. – kstats9pt3 Dec 7 '18 at 19:54
  • 1
    yes you can use substr: cal | awk 'NR>1 && (d=substr($0,2*3,3)+0){print d; exit}' – mosvy Dec 7 '18 at 20:15
  • What about other key dates like the first Thursday, second Monday, third Friday, etc.? The answer works but could you explain the syntax? – kstats9pt3 Dec 7 '18 at 20:18
1

Here's another solution for you

cal | awk 'NR >= 3 { tu = substr($0,7,2) +0 } tu { print tu; exit }'
4

cal 3 2016 | awk 'NR >= 3 { tu = substr($0,7,2) +0 } tu { print tu; exit }'
1

It picks out the Tuesday column (characters 7 and 8), and delivers the first non-zero value. The approach can be safely extrapolated to any day of the week.

  • This is a simple, clean function -- thanks! – kstats9pt3 Dec 10 '18 at 14:06
1

With GNU date:

For the first Tuesday of the current month:

for day in 0{1..7}; do [[ "$(date -d $(date +%Y%m${day}) +%a)" = "Tue" ]] && echo "$day";done

For the first Tuesday of a given year and month:

year=2016
month=03
for day in 0{1..7}; do [[ "$(date -d ${year}${month}${day} +%a)" = "Tue" ]] && echo "$day";done

A "Tuesday" variation in awk:

cal | awk -F "" '!/[[:alpha:]]/ && $7$8 ~ /[0-9]/ { print $7$8;exit }'

This checks the hard-coded position of columns 7 and 8 (Tuesday's) in the output (notice the field separator is null, splitting the input on every character) -- after skipping any lines that have letters in them (the header lines). Adjust the 7 & 8 to get other days.

To make it clearer what the previous command is doing, the awk command's fields are numbered per column:

123456789            <--- awk fields $1, $2, $3, ... $9
    December 2018
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
  • Thanks, Jeff. These examples are great. I'm not sure I understand the 7 & 8 part regarding the columns -- how do these values get the third column in cal? I plugged in other values and don't see the pattern. – kstats9pt3 Dec 7 '18 at 20:14
  • Take a look at the recent edit and see if that helps. – Jeff Schaller Dec 7 '18 at 20:18
  • Ah that makes sense -- thanks for the clarification. We have a lot of files released on first, second, third [day of the week] of the month etc. and some are released but not updated until the following Saturday. Since crontab cannot handle this type of schedule these little functions are perfect. Thanks again. – kstats9pt3 Dec 7 '18 at 20:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.