Passing in an argument within a bash script - bash

I have a bash script that I use to call a java class and I pass two arguments to this java class. The first argument ($1) is the string that I pass and it contains someone's name. The second argument ($2) is the previous month as a two digit number (also passed in by the user).
So the java class is called like this:
java -DCONFIG_DIR=... com.example.myapp.grades.gradingProcess $1 $2
However, now, I don't want the user to pass in the second argument and instead, I want the script to determine the month.
Can I do something like this?
month=`date +'%m' -d 'last month'`
java -DCONFIG_DIR=... com.example.myapp.grades.gradingProcess $1 $month
And when I run my script, it'll be something like this: ./myscript.sh 'John'
and not pass in a two-digit month since I'm already doing it inside the script?
Or is that not the correct way to go about it?
Sorry if this seems like an elementary question, I'm still trying to get used to bash scripts.
Thank you?

If you are looking for how to supply a default value in the shell, there is an operator for that.
month=${2-$(date -d 'last month' +%m)}
java -stuff "$1" "$month"
Now, if there was a value in $2, month will be set to that; otherwise, the default will be used. The notation ${variable-value} supplies the value of variable or, if it is unset, the text value. (There is also ${variable:-value} which produces value if variable is set but empty as well.)
(This could be inlined into the java command line, even, though using a variable to break it up is probably better for legibility.
java -stuff "$1" "${2-$(date -d 'last month' +%m)}"
Notice also how you basically always put user-supplied variables in double quotes.)

A few thoughts: time zone, leading zero, and leveraging Java rather than bash.
Time zone
Determining the current month requires a current date. Determining the current date requires a time zone.
For any given moment the date varies around the globe. For example, a few minutes after midnight in Paris is a new day while still “yesterday” in Montréal.
When you do not specify a time zone, a current default time zone is implicitly applied. Be aware that the default can change at any moment. And depending on the default means your code is relying on an externality outside your direct control. And it means results will vary.
So around the ending/beginning of the month you could be getting the wrong month number determining on the time zone in play.
Leading zero can mean octal in Java
The %m you are using produces two digits. Single digit month numbers will have a leading zero. Ex: 09
Be aware that in some situations in Java a number with a leading zero is interpreted as a octal number (base 8) rather than a decimal number (base 10).
Let Java do the work
I suggest it makes more sense to let the Java class do the work of determining the previous month. The bash line should be passing the desired/expected time zone rather than the month, if anything.
ZoneId zoneId = ZoneId.of( "America/Montreal" ) ;
int previousMonthNumber = LocalDate.now( zoneId ).minusMonths( 1 ).getMonthValue() ;
Tip: In Java, even better to use an object from the Month enum rather than a mere integer. Makes your code more self-documenting, type-safe, and guarantees valid values.
Month month = LocalDate.now( zoneId ).minusMonths( 1 ).getMonth() ;

Related

Format date in AWS's style

I'm looking to compare the dates of some AWS resources in an external script so I need to match AWS's date/time format.
AWS' own documentation states that the date must be the complete date plus hours, minutes, and seconds, however date formats end up looking like 2017-07-27T15:47:59.373Z, where my hardcoding of %Y-%m-%dT%H:%M.%SZ gets me only 2017-07-15T11:39.29Z.
Side by side, that's:
AWS: ??? - 2017-07-27T15:47:59.373Z
Me: %Y-%m-%dT%H:%M.%SZ - 2017-07-15T11:39.29Z
There's something on the end that's adding a few extra digits. What am I missing to get the formatting identical?
The "extra digits" is the milliseconds.
I'm assuming you're using bash's date command. If that's the case, try with:
date -u +"%Y-%m-%dT%H:%M:%S.%3NZ"
The -u option gets the date in UTC (so it's compliant with the Z, which is the UTC designator).
If you don't use -u, it'll print the date and time in the system's timezone (which can't necessarily be UTC).

Ruby: How do I handle time zone offsets that include minutes

I have created an API endpoint that accepts a parameter called time_zone. I use this parameter to determine what time zone the requesting user is in. time_zone is a utc offset value that should be an integer. Example: MST has a UTC offset of -7.
This allows me to insert the passed in time_zone in the following line of code:
start_time = Time.now.in_time_zone(time_zone).beginning_of_day
The above works fine when the time_zone value is something simple like -7. However, if the time zone offset includes minutes, I run into trouble. Example: Venezuela is UTC-04:30. If I pass in '-430', I get ArgumentError: Invalid Timezone.
What value should I be passing into `Time.now.in_time_zone()' to get my example to work?
Thanks.
After much trial and error, I determined that I need to pass a decimal for time zone offsets that include minutes.
So, to address my previous example using Venezuela (UTC-04:30), I can simply pass in the value -4.5 and that works perfectly.
Similarly, for Nepal (UTC+05:45) I would pass in the value 5.75.

how should I got about subtracting two different times in a bash environment

I am currently working on a script that processes and combines several different files, and for the one part, it is necessary that I find the difference between two different times in order to determine a "total" amount of time that someone has worked. the times themselves are in the following format
34:18:00,40:26:00,06:08:00
with the first one being start time, second end time, third total time. Although this one is displayed correctly, there are some entries that need to be double checked and corrected (the total time is not correct based on the start/end time). I have found several different solutions in other posts but most of them also include dates and such too (most of them using awk), I am not experienced with awk so am not sure how to go about removing the date portion from those examples. I have also heard that I could convert the times to unix epoch time, but I was just curious if there were any other ways to accomplish this, thanks!
Something like this might help you:
#!/bin/bash
time2seconds() {
a=( ${1//:/ } )
echo $((${a[0]}*3600+${a[1]}*60+${a[2]}))
}
seconds2time() {
printf "%.2d:%.2d:%.2d" $(($1/3600)) $((($1/60)%60)) $(($1%60))
}
IFS=, read start stop difference <<< "34:18:00,40:26:00,06:08:00"
printf "Start=%s\n" "$start"
printf "Stop=%s\n" "$stop"
printf "Difference=%s (given in file: %s)\n" $(seconds2time $(($(time2seconds $stop)-$(time2seconds $start)))) "$difference"
Output is:
Start=34:18:00
Stop=40:26:00
Difference=06:08:00 (given in file: 06:08:00)
Note: there's nothing that checks if the times are in a valid format, I don't know how reliable your data are.

Yearless Ruby dates?

Is there a way to represent dates like 12/25 without year information? I'm thinking of just using an array of [month, year] unless there is a better way.
You could use the Date class and hard set the year to a leap year (so that you could represent 2/29 if you wanted). This would be convenient if you needed to perform 'distance' calculations between two dates (assuming that you didn't need to wrap across year boundaries and that you didn't care about the off-by-one day answers you'd get when crossing 2/29 incorrectly for some years).
It might also be convenient because you could use #strftime to display the date as (for example) "Mar-3" if you wanted.
Depending on the usage, though, I think I would probably represent them explicitly, either in a paired array or something like YearlessDate = Struct.new(:month,:day). That way you're not tempted to make mistakes like those mentioned above.
However, I've never had a date that wasn't actually associated with a year. Assuming this is the case for you, then #SeanHill's answer is best: keep the year info but don't display it to the user when it's not appropriate.
You would use the strftime function from the Time class.
time = Time.now
time.strftime("%m/%d")
While #Phrogz answer makes perfect sense, it has a downside:
YearlessDate = Struct.new(:month,:day)
yearless_date = YearlessDate.new(5, 8)
This interface is prone to MM, DD versus DD, MM confusion.
You might want to use Date instead and consider the year 0 as "yearless date" (provided you're not a historian dealing with real dates around bc/ad of course).
The year 0 is a leap year and therefore accommodates every possible day/month duple:
Date.parse("0000-02-29").leap? #=> true
If you want to make this convention air tight, just define your own class around it, here's a minimalistic example:
class YearlessDate < Date
private :year
end
The most "correct" way to represent a date without a year is as a Fixnum between 001 and 365. You can do comparisons on them without having to turn it into a date, and can easily create a date for a given year as needed using Date.ordinal

Select time intervals from log files using Bash

I need to extract some information from a log file using a shell script (bash). A line from the log file usually looks like this:
2009-10-02 15:41:13,796| some information
Occasionally, such a line is followed by a few more lines giving details about the event. These additional lines do not have a specific format (in particular they don't start with a timestamp).
I know how to use grep to filter the file based on keywords and expressions. Basically what I'm having trouble with is that sometimes I need to look at specific intervals only. For example I need to look only at the events which happened during the last X minutes. I'm not experienced with shell scripting, but due to the complexity of the time format, this seems to be a rather difficult task for me. On the other hand, I can imagine that this is something not too unusual, so I'm wondering if there are some tools which can make this easier for me or if you can give me some hints on how to tackle this problem?
gawk -F"[-: ]" 'BEGIN{
fivemin = 60 * 60 * 5 #last 5 min
now=systime()
difference=now - fivemin
}
/^20/{
yr=$1
mth=$2
day=$3
hr=$4
min=$5
sec=$5
t1=mktime(yr" "mth" "day" "hr" "min" "sec)
if ( t1 >= difference) {
print
}
}' file
Basically what I'm having trouble with is that sometimes I need to look
at specific intervals only.
You could use date to convert the date signature for you with the %s parameter:
%s seconds since 1970-01-01 00:00:00 UTC
With it we can make a small demonstration:
#!/bin/bash
timespan_seconds=300 # 5 minutes
time_specified=$(date +"%s" -d "2010-08-25 14:54:40")
let time_now=$(date +"%s")
let time_diff=($time_now - $timespan_seconds)
if [ $time_specified -ge $time_diff ]; then
echo "Time is within range"
fi
Note that this doesn't address future time.
You might want to take a look at my Python program which extracts data from log files based on a range of times. The specification of dates is not yet implemented (it is designed to look at roughly the most recent 24 hours). The time format that it expects (e.g. Jan 14 04:10:13) looks a little different than what you want, but that could be adapted. I haven't tested it with non-timestamped lines, but it should print everything within the specified range of times.
This will give you some usage information:
timegrep.py --help

Resources