Weird Thing Happens in a Simple CShell Program - tcsh

Hey fellas, I have this weird bug, and I have no clue how to fix it, would be very glad if you could help.
#!/bin/tcsh -f
set date = ${1}
set time = ${2}
echo 1
set month = `echo $date | cut -f1 -d"-"`
set day = `echo $date | cut -f2 -d"-"`
set year = `echo $date | cut -f3 -d"-"`
echo $date $month $day $year
echo $date $time
if ($year > 69) then
# year = $year + 1900
else
# year = $year + 2000
endif
echo 3
if ($month == "Jan") set month = 01
if ($month == "Feb") set month = 02
if ($month == "Mar") set month = 03
if ($month == "Apr") set month = 04
if ($month == "May") set month = 05
if ($month == "Jun") set month = 06
if ($month == "Jul") set month = 07
if ($month == "Aug") set month = 08
if ($month == "Sep") set month = 09
if ($month == "Oct") set month = 10
if ($month == "Nov") set month = 11
if ($month == "Dec") set month = 12
echo 4
set hour1 = `echo $time | cut -c1`
set hour2 = `echo $time | cut -c2`
set min1 = `echo $time | cut -c4`
set min2 = `echo $time | cut -c5`
set ampm = `echo $time | cut -c6`
echo $hour1 #$hour2 $min1 $min2
if ($ampm =~ [pP]) then
# hour1 = $hour1 + 1
# hour2 = $hour2 + 2
endif
if ($day < 10) then
printf "%s%s0%s%s%s%s%s" $year $month $day $hour1 $hour2 $min1 $min2
else
printf "%s%s%s%s%s%s%s" $year $month $day $hour1 $hour2 $min1 $min2
endif
Basically, what the program does is receives 2 arguments, for example Sep-22-07 11:45am
and returns it in this format yyyymmddhhmm - 200709071145
Now the weird thing happens when I send the 2nd parameter with two zeros at the beginning, like that ... 00:01am, for example. then the whole set month, set day, set year starts to freak and returns me that output:
0.000u 0.001s 0:00.00 0.0% 0+0k 0+0io 0pf+0w
0.000u 0.001s 0:00.00 0.0% 0+0k 0+8io 0pf+0w
0.000u 0.001s 0:00.00 0.0% 0+0k 0+0io 0pf+0w
+ the other output.
There are in the program other echos, but I just used them to debug.
Anyway, thanks in advance. I am kind of a newbie in CShell, so if that's some easy bug I am sorry, but I can't seem to find it.

The command set time (with no arguments) in tcsh is used to enable timing each command executed by the shell. That's the source of the extra output you're seeing. Apparently
set time = 00:01am
or similar has the same effect -- I don't know why the leading "00:" makes a difference,
but it's easily reproducible at the command line.
Since you mention you're a newbie at C shell, I feel somewhat compelled to direct you to this article by Tom Christiansen :
Csh Programming Considered Harmful
Many programmers prefer using Bourne shell or one of its derivatives (ksh, bash...) for non-interactive scripting, to avoid many of the problems described in the above article.

the
set time = x;
allows users to set a threshold for when to log time of commands. If this threshold is set low, it will time all commands that take longer than that threshold. If x is set to a relatively high number, only those which exceed that threshold are timed, and produce the output that you're seeing, hence that is why you only see it when you're setting time to a 00: number. How it deals with they time format you're using and translating that to seconds, however, is beyond my understanding of this.

Related

Fetch Last Friday of Month & Next Sunday

I want to find some information between two dates (Last Friday of Month and it's successive Sunday).
For now, I'm adding all the dates(formatted as mm/dd/yy) in a file dates_file.in like this:
05/29/20,05/31/20
06/26/20,06/28/20
07/31/20,08/02/20
and running a for loop like this
for dates in `cat dates_file.in`
do
date1=`echo $dates | cut -d',' -f1`
date2=`echo $dates | cut -d',' -f2`
xyz_cmd -start="$date1" -end="$date2" -report_parms
done
Is there a way I can use calendar to fetch the dates or export the dates to dates_file.in
file?
OS is RHEL 6.5
Using bash and GNU date:
# a function to return a positive number for "x % y"
# e.g. `floorMod -1 7` outputs: 6
floorMod() { local num=$1 div=$2; echo $(( ((num % div) + div) % div )); }
formatDate() { local y=$1 m=$2 d=$3 delta=$4; date -d "$y-$m-$d $delta" "+%m/%d/%y"; }
year=2020
for month in {1..12}; do
# get the last day of this month, and its day of the week
read -r day dow < <(date -d "$year-$month-01 + 1 month - 1 day" "+%d %w")
# decrement until we find Friday
until ((dow == 5)); do
((day--))
dow=$(floorMod $((dow - 1)) 7)
done
start=$(formatDate $year $month $day)
end=$(formatDate $year $month $day "+2 days")
echo "$start $end"
done
01/31/20 02/02/20
02/28/20 03/01/20
03/27/20 03/29/20
04/24/20 04/26/20
05/29/20 05/31/20
06/26/20 06/28/20
07/31/20 08/02/20
08/28/20 08/30/20
09/25/20 09/27/20
10/30/20 11/01/20
11/27/20 11/29/20
12/25/20 12/27/20
Could you please try following, using cal here.
cat script.ksh
month="$1"
year="$2"
####To get current month's Friday.
cal "$month" "$year" 2>/dev/null |
tac |
awk '
NF>=6{
print "current month last Friday is:"$(NF-1)" and Sunday is:"$NF
exit
}
'

Generate specific date in bash

Hi I have written down some bash to generate the date in format YYYYDDMM.
I know that is not perfect but the final thing will be:
Generate a range of 2 dates that are 31 days apart from each other and start with a minimum of today + one day and the older ones end at the end of this year. In format YYYYMMDD
month="$(awk -v min=1 -v max=12 'BEGIN{srand(); print int(min+rand()*(max-min+1))}')"
day="$(awk -v min=1 -v max=31 'BEGIN{srand(); print int(min+rand()*(max-min+1))}')"
year="$(date +%Y)"
if (( "${month}" < 10 )); then
month_proper="$(echo 0"${month}")"
else
month_proper="$(echo "${month}")"
fi
if (( "${day}" < 10 )); then
day_proper="$(echo 0"${day}")"
else
day_proper="$(echo "${day}")"
fi
echo month "${month}"
echo month with 0 if smaller than 10 : "${month_proper}"
echo day "${day}"
echo day with 0 smaller than 10 : "${day_proper}"
ok="$(date -d ""$year""${month_proper}""${day_proper}"" +"%Y%m%d")"
echo date with proper format "${ok}"
date -d "$year""${month_proper}""${day_proper}"
In which direction would I have to expand this script to get the final result? I already have the date generation, but there is no checking if there is one day ahead of today.
Requirements specify range nearly exactly (it is Dec-31 till Jan-31 next year), so you can write
a_fmt=`date -d "tomorrow" +%Y1231`
a_num=`date -d "$a_fmt" +%s`
b_num=$(( a_num + 31 * 24 * 3600 ))
b_fmt=`date -d #$b_num +%Y%m%d`
echo "Range is '$a_fmt'..'$b_fmt'"

How could I use bash to work out how many tuesdays there are in a month? [duplicate]

I need to sort data on a weekly base and all i have are dates in a logfile.
Therefore to sort out data per week i would like to create a list with the dates of all mondays for a given year. I have tried to work something out and the only idea i currently have is to use ncal with year and month as argument looping over all months and extracting all mondays. Isn't there a more efficient way?
To get all mondays, by getting all dates and filtering by Mondays:
for i in `seq 0 365`
do date -d "+$i day"
done | grep Mon
Of course, you could also take a monday and keep incrementing by 7 days.
hope that's what you mean. Below can be changed to vary the output formats of the dates.
date command can be used for that, dunno if ncal is any more/less efficient.
I know you went for "binning" now, but here is a more readable v.
$ cat /tmp/1.sh
#!/bin/bash
test -z "$year" && {
echo "I expect you to set \$year environment variable"
echo "In return I will display you the Mondays of this year"
exit 1
}
# change me if you would like the date format to be different
# man date would tell you all the combinations you can use here
DATE_FORMAT="+%Y-%m-%d"
# change me if you change the date format above. I need to be
# able to extract the year from the date I'm shoing you
GET_YEAR="s/-.*//"
# this value is a week, in milliseconds. Changing it would change
# what I'm doing.
WEEK_INC=604800
# Use another 3-digit week day name here, to see dates for other week days
DAY_OF_WEEK=Mon
# stage 1, let's find us the first day of the week in this year
d=1
# is it DAY_OF_WEEK yet?
while test "$(date -d ${year}-1-${d} +%a)" != "$DAY_OF_WEEK"; do
# no, so let's look at the next day
d=$((d+1));
done;
# let's ask for the milliseconds for that DAY_OF_WEEK that I found above
umon=$(date -d ${year}-1-${d} +%s)
# let's loop until we break from inside
while true; do
# ndate is the date that we testing right now
ndate=$(date -d #$umon "$DATE_FORMAT");
# let's extract year
ny=$(echo $ndate|sed "$GET_YEAR");
# did we go over this year? If yes, then break out
test $ny -ne $year && { break; }
# move on to next week
umon=$((umon+WEEK_INC))
# display the date so far
echo "$ndate"
done
No need to iterate over all 365 or 366 days in the year. The following executes date at most 71 times.
#!/bin/bash
y=2011
for d in {0..6}
do
if (( $(date -d "$y-1-1 + $d day" '+%u') == 1)) # +%w: Mon == 1 also
then
break
fi
done
for ((w = d; w <= $(date -d "$y-12-31" '+%j') - 1; w += 7))
do
date -d "$y-1-1 + $w day" '+%Y-%m-%d'
done
Output:
2011-01-03
2011-01-10
2011-01-17
2011-01-24
2011-01-31
2011-02-07
2011-02-14
2011-02-21
2011-02-28
2011-03-07
. . .
2011-11-28
2011-12-05
2011-12-12
2011-12-19
2011-12-26
Another option that I've come up based on the above answers. The start and end date can now be specified.
#!/bin/bash
datestart=20110101
dateend=20111231
for tmpd in {0..6}
do
date -d "$datestart $tmpd day" | grep -q Mon
if [ $? = 0 ];
then
break
fi
done
for ((tmpw = $tmpd; $(date -d "$datestart $tmpw day" +%s) <= $(date -d "$dateend" +%s); tmpw += 7))
do
echo `date -d "$datestart $tmpw day" +%d-%b-%Y`
done
You can get the current week number using date. Maybe you can sort on that:
$ date +%W -d '2011-02-18'
07

How to decode Seagate's hard drive date code in a Bash script

Seagate hard drives display a code instead of the manufacturing date. The code is described here and an online decoder is available here.
In short, it's a 4 or 5 digit number of the form YYWWD or YYWD, where:
YY is the year, 00 is year 1999
W or WW is the week number beginning 1
D is day of week beginning 1
Week 1 begins on the first saturday of July in the stated year
Examples
06212 means Sunday 20 November 2005
0051 means Saturday 31 July 1999
How can this be decoded in a bash script ?
This is what I did, it should work:
#!/bin/bash
DATE=$1
REGEX="^(..)(..?)(.)$"
[[ $DATE =~ $REGEX ]]
YEAR=$(( ${BASH_REMATCH[1]} + 1999 ))
WEEK=$(( ${BASH_REMATCH[2]} - 1))
DAYOFWEEK=$(( ${BASH_REMATCH[3]} - 1))
OFFSET=$(( 6 - $(date -d "$YEAR-07-01" +%u) ))
DATEOFFIRSTSATURDAY=$(date -d "$YEAR-7-01 $OFFSET days" +%d)
FINALDATE=`date -d "$YEAR-07-$DATEOFFIRSTSATURDAY $WEEK weeks $DAYOFWEEK days"`
echo $FINALDATE
It worked for the two dates given above...
If you want to customize the date output, add a format string at the end of the FINALDATe assignment.
Here is a short script, it takes two arguments: $1 is the code to convert and $2 is an optional format (see man date), otherwise defaulted (see code).
It uses the last Saturday in June instead of the first one in July because I found it easer to locate and it allowed me to just add the relevant number of weeks and days to it.
#!/bin/bash
date_format=${2:-%A %B %-d %Y}
code=$1
[[ ${#code} =~ ^[4-5]$ ]] || { echo "bad code"; exit 1; }
let year=1999+${code:0:2}
[[ ${#code} == 4 ]] && week=${code:2:1} || week=${code:2:2}
day=${code: -1}
june_last_saturday=$(cal 06 ${year} | awk '{ $6 && X=$6 } END { print X }')
date -d "${year}-06-${june_last_saturday} + ${week} weeks + $((${day}-1)) days" "+${date_format}"
Examples:
$ seadate 06212
Sunday November 20 2005
$ seadate 0051
Saturday July 31 1999
I created a Seagate Date Code Calculator that actually works with pretty good accuracy. I've posted it here on this forum for anyone to use: https://www.data-medics.com/forum/seagate-date-code-conversion-translation-tool-t1035.html#p3261
It's far more accurate than the other ones online which often point to the entirely wrong year. I know it's not a bash script, but will still get the job done for anyone else who's searching how to do this.
Enjoy!

How to generate a sequence of dates given starting and ending dates using AWK of BASH scripts?

I have a data set with the following format
The first and second fields denote the dates (M/D/YYYY) of starting and ending of a study.
How one expand the data into the desired output format, taking into account the leap years using AWK or BASH scripts?
Your help is very much appreciated.
Input
7/2/2009 7/7/2009
2/28/1996 3/3/1996
12/30/2001 1/4/2002
Desired Output
7/7/2009
7/6/2009
7/5/2009
7/4/2009
7/3/2009
7/2/2009
3/3/1996
3/2/1996
3/1/1996
2/29/1996
2/28/1996
1/4/2002
1/3/2002
1/2/2002
1/1/2002
12/31/2001
12/30/2001
It can be done nicely with bash alone:
for i in `seq 1 5`;
do
date -d "2017-12-01 $i days" +%Y-%m-%d;
done;
or with pipes:
seq 1 5 | xargs -I {} date -d "2017-12-01 {} days" +%Y-%m-%d
If you have gawk:
#!/usr/bin/gawk -f
{
split($1,s,"/")
split($2,e,"/")
st=mktime(s[3] " " s[1] " " s[2] " 0 0 0")
et=mktime(e[3] " " e[1] " " e[2] " 0 0 0")
for (i=et;i>=st;i-=60*60*24) print strftime("%m/%d/%Y",i)
}
Demonstration:
./daterange.awk inputfile
Output:
07/07/2009
07/06/2009
07/05/2009
07/04/2009
07/03/2009
07/02/2009
03/03/1996
03/02/1996
03/01/1996
02/29/1996
02/28/1996
01/04/2002
01/03/2002
01/02/2002
01/01/2002
12/31/2001
12/30/2001
Edit:
The script above suffers from a naive assumption about the length of days. It's a minor nit, but it could produce unexpected results under some circumstances. At least one other answer here also has that problem. Presumably, the date command with subtracting (or adding) a number of days doesn't have this issue.
Some answers require you to know the number of days in advance.
Here's another method which hopefully addresses those concerns:
while read -r d1 d2
do
t1=$(date -d "$d1 12:00 PM" +%s)
t2=$(date -d "$d2 12:00 PM" +%s)
if ((t2 > t1)) # swap times/dates if needed
then
temp_t=$t1; temp_d=$d1
t1=$t2; d1=$d2
t2=$temp_t; d2=$temp_d
fi
t3=$t1
days=0
while ((t3 > t2))
do
read -r -u 3 d3 t3 3<<< "$(date -d "$d1 12:00 PM - $days days" '+%m/%d/%Y %s')"
((++days))
echo "$d3"
done
done < inputfile
You can do this in the shell without awk, assuming you have GNU date (which is needed for the date -d #nnn form, and possibly the ability to strip leading zeros on single digit days and months):
while read start end ; do
for d in $(seq $(date +%s -d $end) -86400 $(date +%s -d $start)) ; do
date +%-m/%-d/%Y -d #$d
done
done
If you are in a locale that does daylight savings, then this can get messed up if requesting a date sequence where a daylight saving switch occurs in between. Use -u to force to UTC, which also strictly observes 86400 seconds per day. Like this:
while read start end ; do
for d in $(seq $(date -u +%s -d $end) -86400 $(date -u +%s -d $start)) ; do
date -u +%-m/%-d/%Y -d #$d
done
done
Just feed this your input on stdin.
The output for your data is:
7/7/2009
7/6/2009
7/5/2009
7/4/2009
7/3/2009
7/2/2009
3/3/1996
3/2/1996
3/1/1996
2/29/1996
2/28/1996
1/4/2002
1/3/2002
1/2/2002
1/1/2002
12/31/2001
12/30/2001
Another option is to use dateseq from dateutils (http://www.fresse.org/dateutils/#dateseq). -i changes the input format and -f changes the output format. -1 must be specified as an increment when the first date is later than the second date.
$ dateseq -i %m/%d/%Y -f %m/%d/%Y 7/7/2009 -1 7/2/2009
07/07/2009
07/06/2009
07/05/2009
07/04/2009
07/03/2009
07/02/2009
$ dateseq 2017-04-01 2017-04-05
2017-04-01
2017-04-02
2017-04-03
2017-04-04
2017-04-05
I prefer ISO 8601 format dates - here is a solution using them.
You can adapt it easily enough to American format if you wish.
AWK Script
BEGIN {
days[ 1] = 31; days[ 2] = 28; days[ 3] = 31;
days[ 4] = 30; days[ 5] = 31; days[ 6] = 30;
days[ 7] = 31; days[ 8] = 31; days[ 9] = 30;
days[10] = 31; days[11] = 30; days[12] = 31;
}
function leap(y){
return ((y %4) == 0 && (y % 100 != 0 || y % 400 == 0));
}
function last(m, l, d){
d = days[m] + (m == 2) * l;
return d;
}
function prev_day(date, y, m, d){
y = substr(date, 1, 4)
m = substr(date, 6, 2)
d = substr(date, 9, 2)
#print d "/" m "/" y
if (d+0 == 1 && m+0 == 1){
d = 31; m = 12; y--;
}
else if (d+0 == 1){
m--; d = last(m, leap(y));
}
else
d--
return sprintf("%04d-%02d-%02d", y, m, d);
}
{
d1 = $1; d2 = $2;
print d2;
while (d2 != d1){
d2 = prev_day(d2);
print d2;
}
}
Call this file: dates.awk
Data
2009-07-02 2009-07-07
1996-02-28 1996-03-03
2001-12-30 2002-01-04
Call this file: dates.txt
Results
Command executed:
awk -f dates.awk dates.txt
Output:
2009-07-07
2009-07-06
2009-07-05
2009-07-04
2009-07-03
2009-07-02
1996-03-03
1996-03-02
1996-03-01
1996-02-29
1996-02-28
2002-01-04
2002-01-03
2002-01-02
2002-01-01
2001-12-31
2001-12-30
You can convert date to unix timestamp and then sequencing on it, you can even have granularity of nanoseconds if you want (with '%N' in date)
The following example prints time from 2020-11-07 00:00:00 to 2020-11-07 01:00:00 in intervals of 5 minutes
# total seconds past 1970-01-01 00:00:00 as observed on UTC timestamp in UTC
# you change TZ to represent time in your timezone like TZ="Asia/Kolkata"
start_time=$(date -u -d 'TZ="UTC" 2020-11-07 00:00:00' '+%s')
end_time=$(date -u -d 'TZ="UTC" 2020-11-07 01:00:00' '+%s')
# 60 seconds * 5 times (i.e. 5 minutes)
# you change interval according your needs or leave it to show every second
interval=$((60 * 5))
# generate sequence with intervals and convert back to timestamp in UTC
# again change TZ to represent timein your timezone
seq ${start_time} ${interval} ${end_time} |
xargs -I{} date -u -d 'TZ="UTC" #'{} '+%F %T'

Resources