0

I am trying to manipulate cal command to give me output in a single line with date and just below the day of the week so I can use it in excel for a script or html. Like below.

Wed Thu Fri Sat Sun Mon Tue Wed
 1   2   3   4   5   6   7   8
  • The post didn't show the correct output, the way i wanted is date and just below it the day of the week. – Sid Feb 7 '17 at 8:33
  • 1
    so output would be in two lines? – Mc Kernel Feb 7 '17 at 8:37
  • yes, sort of, but the alignment should also be proper so it can be exported to csv,excel or html, because these will not be only lines in the sheet. – Sid Feb 7 '17 at 8:56
  • Let me give some brief, it will be like some columns at the beginning with some title and then adjacent to the date and below it day column title and from there the format i wanted, as there will be entries in the initial columns, this date,day is the tracker which will have entries from my script. make sense? – Sid Feb 7 '17 at 8:57
  • @Sid Please update the question rather than giving details in comments. – Kusalananda Feb 7 '17 at 9:51
1

Using bash (or ksh93), and GNU date:

for (( i = 0; i < 8; ++i )); do
    printf '%s\t' "$( date -d "now +$i days" +"%a" )"
done

echo

for (( i = 0; i < 8; ++i )); do
    printf ' %d\t' "$( date -d "now +$i days" +"%e" )"
done

echo

The %a format will give you the abbreviated day of the week in the current locale, while %e gives you the day of the month as an integer.

Result (tab-separated):

Tue     Wed     Thu     Fri     Sat     Sun     Mon     Tue
 7       8       9      10      11      12      13      14

UPDATE for ksh93 after comments: (note, this doesn't work since the shell doesn't seem to verify the dates properly)

#!/bin/ksh93

yearmonth=$( date +"%Y%m" )

d=1
while printf '%(%a)T\t' "$( printf '%s%02d' "$yearmonth" "$d" )" 2>/dev/null
do
    (( ++d ))
done
echo

days_this_month=$(( --d ))
while (( d > 0 )); do
    printf ' %d\t' "$(( days_this_month - (--d) ))"
done
echo

yearmonth is first set to the current year and month on the YYYYMM format. I then loop over all weekdays of this month. The loop sets wday to the abbreviated weekday, and it will terminate when I try to get the weekday of a date that is invalid.

The second loop just prints integers, as many as we need to fill out the month.

Output for February 2017:

Wed     Thu     Fri     Sat     Sun     Mon     Tue     Wed     Thu     Fri     Sat     Sun     Mon     Tue     Wed     Thu     Fri     Sat     Sun     Mon     Tue     Wed     Thu     Fri     Sat     Sun     Mon     Tue
 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

Solution using ksh93 with Perl and Date::Calc:

#!/bin/ksh93

ym=( $(date +"%Y %m") )

days=$( perl -MDate::Calc -e 'print Date::Calc::Days_in_Month($ARGV[0],$ARGV[1])' -- "${ym[0]}" "${ym[1]}" )

for d in {1..$days}; do
    printf '%(%a)T\t' "$( printf '%d-%d-%02d' "${ym[@]}" "$d" )"
done
echo

for d in {1..$days}; do
    printf '%d\t' "$d"
done
echo
|improve this answer|||||
  • Thanks Kusalananda, Would it work month wise? as in the for loop should take maximum limit as the month's last day (30/31/28). Moreover it will display from current date, what if i want from 1st day of every month irrespective of what day i choose to run the script? Because this way I will have to make sure to run it on first day of the month which i can achieve from crontab. – Sid Feb 7 '17 at 10:19
  • @StéphaneChazelas I don't usually mention zsh in my answers as it's a shell I don't use on a daily basis. Yes, it could easily be converted to plain sh, but the syntax is not as neat. Thanks for pointing me to the %(...)T format -- useful. – Kusalananda Feb 7 '17 at 10:41
  • @Sid It was not clear from the question what you wanted. I may update my answer in a short while. – Kusalananda Feb 7 '17 at 10:42
  • 1
    Sorry, my bad, I was trying the stress that for((;;)) was in ksh88 as well which was wrong, as that was added in ksh93. I keep getting that one wrong. Comment removed. – Stéphane Chazelas Feb 7 '17 at 11:34
  • @Sid Update my answer. – Kusalananda Feb 7 '17 at 11:36
1

With zsh alone (only builtins):

#! /bin/zsh -
zmodload zsh/datetime
now=$EPOCHSECONDS
strftime -s year %Y $now
strftime -s month %m $now
strftime -rs s %Y,%m,%d,%H $year,$month,1,12
output=()
while
  for field (a d m) strftime -s $field %-$field $s
  ((month == m))
do
  output+=($a $((d)))
  ((s += 86400))
done
print -C $(($#output / 2)) $output

With bash (4.2 or above) alone (only builtins), but assuming the week day abbreviations are 3 characters or less and are all made of single-byte characters only in your locale (so won't work properly in many locales beside English ones).

#! /bin/bash -
printf -v now '%(%s)T' -1

printf -v d '%(%-d)T' "$now"
printf -v h '%(%-H)T' "$now"
printf -v month '%(%-m)T' "$now"

s=$((now +(12-h)*3600 - (d-1) * 86400))

l1= l2=

while
  for field in a d m; do printf -v "$field" "%(%-$field)T" "$s"; done
  ((month == m))
do
  printf -v l1 '%s%-3s ' "$l1" "$a"
  printf -v l2 '%s%-3s ' "$l2" "$d"
  ((s += 86400))
done
printf '%s\n' "$l1" "$l2"
|improve this answer|||||
0

You can also just extract the corresponding lines directly from the cal output, as follows:

>cal|sed -n '2p;/_/p'
Su Mo Tu We Th Fr Sa  
 5  6  7  8  9 10 11

(but note, that cal will use the two-letter weekday abbreviations, at least on my system)

|improve this answer|||||
0
$ cal | perl -0ne '
   if(/(.*)(Su.*?)\n(\h*)( 1.*?)\s*$/s){
      $s= length($3); 
      $b=$2 x 5; 
      $n= $4 =~ s/[^\d ]//gr; 
      print substr($b,$s,length($n)),"\n$n\n" }'

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

Explanation: the output of cal is divided by the regular expression (.*)(Su.*?)\n(\h*)( 1.*?)

 Febr 2017  \nSu Mo Tu We Th Fr Sa  \n          1 2 3 4 \n 5 6 7 8 9 10 11 ...
($1         )($2                   )  ($3     )($4                         ... )

  • $2 is used to build the bar of the weeks ($b = $2 x 5)
  • $4 is used to build the list of the days, after removing \n and highlighting characters ($n= $4 =~ s/[^\d ]//gr)
  • the length of $3 is used to remove the last month' days out of the bar of the weeks (substr($b,$s,length($n)))
|improve this answer|||||

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.