Shell Scripting - Hour and Minute Computation - linux

I am working on a shell script computation program wherein I want a script that computes for the total number of hours and minutes a particular user has been logged on to the system. The script accepts one parameter, the login name. Information like the complete name of the user, the current month and the dates the user logged on earliest during the month and the latest during the month. In the pseudo, I wanted somewhat like this:
[prompt]$ <your familyname>user_login mycroft<enter>
For the Month of June :
User : mycroft
In Real life : Mycroft Holmes
period : Mon Jun 16 to Sat Jun 28
total login time : 2 hours and 2 minutes
Is there a possible way to do this in Shell? I have been on blanks and errors until now.

Look at who or finger program. Probably it's all you need.

Related

Working with files based on filename calulated from time

I have a remote Linux computer, a raspberry pi, that snaps two pictures a minute and uploads them to a Linux server. The photos are named like this: SITE-03-22-16-091543.jpeg. With the filename being formatted like: Sitename-month-day-year-hourminutesecond.jepg. Before the photo is sent, via scp, I embed some local weather date into each photo using exiv2. That way the weather conditions are stored within each photo. All of that is working fine. I hope to have about 15 of these all sending back two snaps a minute to the server.
On the server side, these photos are stored within their own SITE folder. The idea is to make time-lapse videos from each site. There are four types of time-lapses we are interested in:
1) A 24 hour loop, from 12:00am to 11:59pm.
2) A sunrise loop, from 30 minutes before sunrise to 2 hours past sunrise
3) A sunset loop, from 2 hours before sun set to 30 minutes past sunset
4) A daylight loop, from 30 minutes before sunrise to 30 minutes past sunset
The 24 hour loop is simple.
The sunrise and sunset loops are a little trickier. I downloaded and complied the “sunwait” program from Ian Craig on SourceForge (https://sourceforge.net/projects/sunwait4windows/). Using the command “sunwait list rise 35.1174N 89.9711W | gawk -F: '{ print $1$2 }'” produces the output 0700, sun rise at my location. And using the 'set' option, produces 1913, sunset at my location. Since I don't live at the equator, the sunrise and sunset vary from 5:30am to 7:30am. Depending on season. Of course.
I have the code to compile a list of images into the move, add on overlay, and add the embedded weather data. The question is how to create a list of the 30 minutes of pictures before the sunrise + 2 hours. Then 2 hours before sunset + 30 minutes past. Then finally, 30 minutes before sunrise all the way through sunset + 30 minutes.
I'm sure the answer is MATH! Can someone start me on the yellow brick road?
awk to the rescue!
substituting your script to generate time by echo here
$ echo 07:10 |
awk -F: -v offset=30 -v path="$filepath" '{
h=$1-int(offset/60);
m=$2-offset%60;
if(m<0) {m=m+60; h--}
for(i=0;i<=150;i++)
{m++;
if(m>59) {m=m%60; h++};
printf path"%02d%02d.jpeg\n",h,m}}'
creates a 151 step counter that starts from offset (in minutes) given hours minutes. For the other case enter offset as 120. Assumes start/end times doesn't change the date. May not be true around North Pole!
I think some of the variables can be simplified, but can be a working base for further improvements.
update: int() was missing, fixed, also you can pass the path as another variable

NodaTime supports relative time display?

I have a situation where the relative time is more important to a user than an absolute time. So it's more important to be able to quickly say "event happened 5 days and 5 hours ago" than "event happened at 1 PM CDT and it's 5 PM CST 5 days later now."
We store our dates in UTC and convert to display for the user:
pDateTime = DateTime.SpecifyKind(pDateTime, DateTimeKind.Utc);
DateTimeZone dateTimeZone = DateTimeZoneProviders.Tzdb[pCurrentUser.PreferredTimezone];
return Instant.FromDateTimeUtc(pDateTime).InZone(dateTimeZone).ToString("HH':'mm':'ss' 'MM'/'dd'/'yy' 'x", CultureInfo.InvariantCulture);
We'll be using NodaTime 1.2 when it's fully out and just used vanilla ToString before.
However, times using this pattern end up using the daylight status of the time as opposed to the current daylight status. This means that times look something like: 16:15:32 10/25/13 CDT even though we have now transitioned to CST.
It is an absolute measure of the time. This forces the user to do the logic: "How long ago was that? Is it daylight saving time now? If so, the difference is x. If not, I have to add or subtract an hour? That makes the difference y."
Meanwhile, a relative measure of the time would display 15:15:32 10/25/13 CST in the absence of DST. This forces the user to do no conversions and allows them to compute what that time means in context much easier.
In a display that has numerous dates, it can get tricky to do the absolute time logic over the entire set. Doing it once is tricky to get right. However, a friendly relative string like "posted 5 hours ago" also forces them to resolve both the date and time themselves - that information is still important.
A compromise might be to do the posted blank hours/minutes ago for the first 24 hours or to include both the friendly string and absolute time - these are both patterns I've seen done.
But ignoring those, is there a way in NodaTime to imbue a time with a specific daylight status in order to get times displaying in a relative context?
However, times using this pattern end up using the daylight status of the time as opposed to the current daylight status. This means that times look something like: 16:15:32 10/25/13 CDT even though we have now transitioned to CST.
Yes, and it should. Displaying a date/time with CST despite that date/time occurring in CDT would be very odd, IMO.
So it's more important to be able to quickly say "event happened 5 days and 5 hours ago" than "event happened at 1 PM CDT and it's 5 PM CST 5 days later now."
In that case you shouldn't be displaying a date/time at all, in my view. Convert both ZonedDateTime values to Instant, take the Duration between them, and then you can see that it's 5 days and 5 hours ago. (I can't remember how much help we provide with that - you may need to manually take the number of ticks and divide by NodaConstants.TicksPerStandardDay etc. Look at DurationPattern to see if it helps though.)
Alternatively, if you really want to display a date and time, but still easily be able to extract the difference between them mentally, two options suggest themselves:
Use OffsetDateTime instead; there you could force the offsets to be the same, although I still think it would be odd to display an offset which wasn't actually the current offset in the zone you were observing the time in. Or you could just display the relevant offset at the time, so -5 for CST and -4 for CDT.
Just display everything in UTC, so that daylight saving transitions are irrelevant.
Note that you can't get months between the two ZonedDateTime values, as we're dealing with an elapsed time (a duration) rather than calendar-logical arithmetic (a period).

Run job every month on a specific day (with anacron?)

I would like to run jobs once a month on, let's say, the 22nd day of the month, on my laptop running Ubuntu 12.04.
Since it's a laptop, and I may not always even use it every 22nd day of each month, cron is not a very good option.
Looking into anacron, there seems to be a limitation. Namely, you can specify a 'period', but not a specific day of the week or day of the month, as suggested by the anacrontab file format:
# cat /etc/anacrontab
period delay job-identifier command
7 15 test.daily /bin/sh /home/myself/backup.sh
I would like to be able to say, if we're on the 22nd day of the month, and of course the laptop is running, run the job. If the 22nd is passed and you have not run the job, run it as soon as I boot.
I am about to do something ugly, like mixing cron and anacron with custom scripts or writing my own bash script, using timestamps, probably reinventing the square wheel in the process.
Any idea about a best course of action?
Cheers.
Run the command daily, and have the script record the date that it last performed a backup
When it starts up, get the current day of month. If it's the 22nd oh the month, run normally and save the date. If it's >22, and the last run was in the same month, exit. If it's <22, and the last run was the previous month (don't forget to account for wrapping from 12 to 1), exit.
The date should be saved in a file somewhere.

How should timestamps in last output be interpreted on linux?

$ last -aid
foouser pts/12 Sun Feb 15 07:30 - 15:23 (2+07:52) XXX.YYY.ZZZ.QQQ
The Sun Feb 15 07:30 - 15:23 part is pretty obvious. The user logged in at 7:30 on Sunday. But (2+07:52) is unclear. 7:52 is the difference between the two times, but what's the 2+ part?
Use the source, Luke:
sprintf(length, "(%d+%02d:%02d)", days, hours, mins);
This is from the sysvinit source package on Debian.
Hint for those without knowledge in C: it's the number of days.
The string between the parentheses is the duration of the session. As you note, the time is the difference between the end and start times. The 2+ is the number of days as hop noted.
At some point, this question probably should be moved over to Server Fault since it's most likely to be of interest to System Administrators.
It's a fairly readable format once you know what you are looking at. If I were designing the output, I think I'd make the day of the week optional and put the entire end date in the string instead. Even better would be to let the date stamp be configurable. That way, the output could be more easily used by another program.
The actual duration, if the user has logged out of the session is fairly easy to pick out with a regular expression:
$ last | perl -lne 'print "$2 days, $3 hours, $4 minutes" if /\(((\d+)\+)(\d{2}):(\d{2})\)$/'
17 days, 05 hours, 23 minutes
3 days, 23 hours, 16 minutes
14 days, 06 hours, 09 minutes
23 days, 04 hours, 54 minutes
16 days, 06 hours, 57 minutes
...
Those might be days but I'm not sure; also this is not a programming question.

Does cron expression in unix/linux allow specifying exact start and end dates

I want to be able to configure something like this.
I want to run job 'X' at 7 AM everyday starting from 29/june/2009 till 30/12/2009. Consider current date as 4/4/2009.
It can be done in a tricky sort of way.
You need three separate cron jobs for that range, all running the same code (X in this case):
one for the 29th and 30th of June ("0 7 29,30 6 * X").
one for every day in the months July through November ("0 7 * 7-11 * X").
one for all but the last day in December ("0 7 1-30 12 * X").
This gives you:
# Min Hr DayOfMonth Month DayOfWeek Command
# --- -- ---------- ----- --------- -------
0 7 29,30 6 * X
0 7 * 7-11 * X
0 7 1-30 12 * X
Then make sure you comment them out before June 29, 2010 comes around. You can add a final cron job on December 31 to email you that it needs to be disabled.
Or you could modify X to exit immediately if the year isn't 2009.
if [[ "$(date +%Y)" != "2009" ]] ; then
exit
fi
Then it won't matter if you forget to disable the jobs.
Yes, mostly. Some cron implementations have support for years, some don't, so we'll assume yours does not. Also, I'm making the assumption that this job is only being run by the cron daemon, so we can use the execute bit to determine whether or not cron should run the job.
Note that you'll need to leave your script as non-executable until such time as you want it to run.
The following cron expressions will do what you want (every day, including weekends). Tweak as you need to:
# Make the job executable on 29 June.
0 6 29 6 * chmod +x /path/to/my/job/script
# Run the job between June and December, only if it's executable.
0 7 * 6-12 * test -x /path/to/my/job/script && /path/to/my/job/script
# Disable execution after 30 December.
0 8 30 12 * chmod -x /path/to/my/job/script
I'm usually a fan of keeping the logic with the program being run. You might think about setting up one cron job that runs the script every day, then have the script decide on its own whether or not it should do anything useful. When the last useful day (Dec 30) has passed, your script could remove itself from the crontab. In the script you can set up the logic with all the comments necessary to describe what you are doing and why.
If your job is a binary program, you might set up a run_script that does this schedule filtering work before calling the program.
You can use this to generate a crontab that runs at specific intervals:
http://www.robertplank.com/cron/
Or this
http://www.webmaster-toolkit.com/cron-generator.shtml
One solution would be to setup 6 crons, 1 for each month, each would run at 7 am every day that month.
It's probably the easiest way, the next one up would be to script it.
No, afaik, you cannot do that.
The cron fields hold the values for minutes, hours, day of month, month and day of week, respectively.
10 5 10 * * means run at 5:10 on every 10th of every month.
10 5 * 12 * means run at 5:10 on every day in december
10 5 * * 1 means run at 5:10 every Monday
You can make it run on a series of specific months, as the crontab format does accept ranges. April through December would be 4-12 in that case for the month field. But that does not take into account your wish for having this limited to 2009.
There is no mechanism to set start and stop dates for cronjob. You can always script this of course. Make a cronjob run every day and check the current date to be before 30/12. If it is 31/12 make it remove itself. Or something more thought through.
A crontab of
0 7 * * 6-12 command_X
would do what you want partially, but it would start at June 1st and run through December 31st. Skipping the first part of June and December 31st would have to be scripted in the X command.

Resources