Question

I am currently using this command,

cal $month $year | sed -n '1,2p'
cal $month $year | sed -n '3,$p' |
    sed -n '/'$day'/{s/.*\('$day'.*\)/\1/p; :a; n; p; ba; }'

And it is giving me this output

    March 2014
Su Mo Tu We Th Fr Sa
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

How can I get this output?

    March 2014
Su Mo Tu We Th Fr Sa
       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

FYI: $month $year $day is using the current date. I am trying to avoid using a set number of spaces because if it was a different day then the numbers would not match up with the spaces.

EDIT: For Jonathan Leffler

Thank you! This is getting really close to the output I am looking for. The sample output you posted is exactly what I am looking for but after trying your code. It gave me this instead.

March 2014 Su Mo Tu We Th Fr Sa 2 3 4 5 Q6 7 8 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

How am I able to remove the line with the Q? I assume this is coming from the 2nd s/// you provided

EDIT:

Figure it out, Thank you for your help!

Was it helpful?

Solution

This script works (I think):

year=2014
month=3
day=6
cal $month $year | sed -n -e '
1,2p
3,${/'$day'/{
  s/^\(.*\)\('$day'.*\)/\1Q\2/
  :blanks
  s/^\( *\)[^ Q]/\1 /g
  t blanks
  s/Q//p
  :a
  n
  p
  ba
  }
}'

Sample output:

     March 2014       
Su Mo Tu We Th Fr Sa  
             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
  • The first s/// command puts a Q (not part of the output from cal) before the day that you want to keep.
  • The label :blanks, the s/// and the t blanks replace a string of blanks and a non-blank, non-Q with the string of blanks and another blank, zapping all the non-blank characters before the Q.
  • The s/Q//p removes the marker and prints the line.
  • The remainder of the code is the same as before (but spread over multiple lines); it gets the next line of input and prints it repeatedly.

OTHER TIPS

An awk script to acheive the desired effect:

cal.awk:

# Print first two lines (heading)
NR < 3 { print; next } 

# Skip weeks in which the last day is before variable 'day'
$NF < day { next }

# Print full weeks if the first day is on or after variable 'day'
$1 >= day { print; next }

# This will be executed for the week on which variable 'day' falls.
{ 
  # Traverse each day
  for (i=1; i<=NF; ++i) {
    # If current day is on or after variable 'day', print it.
    if ($i >= day) {
      if ($i < 10) # Check for extra formatting for single digit days
        printf(" ");
      printf("%d ", $i); 
    }
    else 
      printf "   "; # Formatting for blank days
  } 
  print ""; # Add new line
} 

Invocation:

cal $month $year | awk -v day=$day -f cal.awk
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top