Compare the days of the week in unix scripting - string

I want to compare the current day of the week and execute a set of statements depending on day.
to_day=$(date +%a)
if[ "$to_day" = "Sun" ]
then
echo "Today is sunday"
echo "First day of the week"
elif[ "$to_day" = "Mon" ]
then
echo "Today is monday"
echo "Second day of the week"
and so on...
I have tried the below if formats
if[$to_day = "Tue"]
if["$to_day" -eq "Tue"]
if["$to_day" == 'Tue']
if["$to_day" = 'Tue']
But the error is still present and it reads "if[ Tue = Tue ]:Command not fount ". I have tried the above with spaces after the braces also.

Make sure in your first line is:
#!/bin/bash
and if it is in your code then make sure if: /bin and /usr/bin or /usr/local/bin directories are in you path, because all commands are here. You can check with:
$ echo $PATH
and you will get a similar answer:
/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11R6/bin:/usr/local/bin:/home/vivekgite/bin
but if you don't have those paths, you can add theirs with:
$ export PATH=$PATH:/bin:/usr/local/bin

Related

How to judge whether today is Sunday in Linux Shell Scripts?

I use these to judge Sunday in shell:
week=`date -d $day +%w`
month=`date -d "-1 day ago $day" '+%d'`
if [ "$week" = "0" ];then
if [ "$month" = "01" ];then
But when $day is 20171103 , It was regarded as Sunday , then the 20171105 was missed.
Then I run "date -d 20171105 +%w" in the server ,it shows 0, and the if caluse was true. Then I check the Time Zone , Time Zone was right ,+0800
Are there any better methods in shell that can solve this problem? More safer and robust to judge whether the string like YYYYMMDD is Sunday?
Why not just use date %u for day of the week?
There are plenty of options in the man page.

0999: Value too great for base (error token is "0999")

This is a shortened-version of a script for reading 8mm tapes from a EXB-8500 with an autoloader (only 10 tapes at a time maximum) attached. It dd's in tape data (straight binary) and saves it to files that are named after the tape's 4-digit number (exmaple D1002.dat) in both our main storage and our backup. During this time it's logging info and displaying its status in the terminal so we can see how far along it is.
#!/bin/bash
echo "Please enter number of tapes: [int]"
read i
j=1
until [ $i -lt $j ]
do
echo "What is the number of tape $j ?"
read Tape_$j
(( j += 1 ))
done
echo "Load tapes into the tower and press return when the drive is ready"
read a
j=1
until [ $i -lt $j ]
do
k="Tape_$j"
echo "tower1 $j D$(($k)) `date` Begin"
BEG=$j" "D$(($k))" "`date`" ""Begin"
echo "tower1 $j D$(($k)) `date` End"
END=$j" "D$(($k))" "`date`" ""End"
echo "$BEG $END"
echo "$BEG $END"
sleep 2
(( j += 1 ))
done
echo "tower1 done"
Everything was hunky-dory until we got under 1000 (startig at 0999). Error code was ./tower1: 0999: Value too great for base (error token is "0999"). Now I already realize that this is because the script is forcing octal values when I type in the leading 0, and I know I should insert a 10# somewhere in the script, but the question is: Where?
Also is there a way for me to just define Tape_$j as a string? I feel like that would clear up a lot of these problems
To get the error, run the script, define however many tapes you want (at least one, lol), and insert a leading 0 into the name of the tape
EXAMPLE:
./test
Please enter number of tapes: [int]
1
What is the number of tape 1?
0999
./test: 0999: Value too great for base (error token is "0999")
You don't want to use $k as a number, but as a string. You used the numeric expression to evaluate a variable value as a variable name. That's very bad practice.
Fortunately, you can use variable indirection in bash to achieve your goal. No numbers involved, no error thrown.
echo "tower1 $j ${!k} `date` Begin"
BEG=$j" "D${!k}" "`date`" ""Begin"
And similarly in other places.

Shell variable value in pre-defined range

I want a variable whose value should be in already defined range.
for e.g.
variable DAY=$1
should only get values Mon,Tue,Wed,Thus,Fri,Sat,Sun.
if user enter something else, it will not accept it.
I know i can do this by defining array or normally a variable storing all the days and then check variable DAY in a "for or while" loop.
But i want the simplest way without loop.
Thanks in advance.
Use the extended glob support to match exactly one of a list of choices.
range="Mon|Tue|Wed|Thu|Fri"
read day
if [[ $day = #($range) ]]; then
echo "$day is valid"
else
echo "$day is not valid"
fi
Without using a loop you can do something like this:
EOL=$'\n'
arr=(Mon Tue Wed Thus Fri Sat Sun)
read -p 'Enter day value: ' day
Tue
[[ $(printf "$EOL%s$EOL" "${arr[#]}") == *"$EOL$day$EOL"* ]] &&
echo "in range" || echo "not in range"
in range
read -p 'Enter day value: ' day
ue We
[[ $(printf "$EOL%s$EOL" "${arr[#]}") == *"$EOL$day$EOL"* ]] &&
echo "in range" || echo "not in range"
not in range
There's really no need for an array.
read -r day
case ' Mon Tue Wed Thu Fri Sat Sun ' in
*" $day "*)
echo "in range";;
*) echo "not in range";;
esac
Notice the spaces around the string expressions.
If you need to be strict, you should reject "Tue Wed" which this doesn't do. It would require a second case wrapper, which is clunky but not completely unwieldy.
To expand slightly on anubhava's answer:
arr=(Mon Tue Wed Thus Fri Sat Sun)
read -p 'Enter day value: ' day
case "${arr[#]}" in *"$day"*) echo "in range";; esac
Basically, this is building an array, turning it into a string, and doing a pattern match on it.
In the context of a case expression, there's no difference between "${arr[#]}" and "${arr[*]}"; I personally would stick with *, which makes it clearer that you're building one big string instead of a list of separate ones.
With this specific set of values, you don't have to worry about substring overlap, but the above check is still overly forgiving. For instance, if you enter u, it will be considered "in range". That may not be what you want.
A more exact test would be something like this:
case "${arr[*]}" in
"$day "*|*" $day "*|*" $day") echo "in range";;
*) echo "not in range";;
esac
This still permits the user to enter multiple days as "Mon Tue" or similar; an easy way to fix that in this particular case is to change the read line from read day to read day _.
The "$day "* pattern will match at the beginning of the array (so if they enter Mon in this case). The *" $day" pattern will match at the end of the array (Fri), and the *" $day "* pattern will match the rest. Nothing else but an exact day string will match.
Also, here you can see how to handle the else case - a pattern of * matches anything that hasn't already matched something else, so that's the equivalent of else in a case.

How can you can calculate the time span between two time entries in a file using a shell script?

In a Linux script: I have a file that has two time entries for each message within the file. A 'received time' and a 'source time'. there are hundreds of messages within the file.
I want to calculate the elapsed time between the two times.
2014-07-16T18:40:48Z (received time)
2014-07-16T18:38:27Z (source time)
The source time is 3 lines after the received time, not that it matters.
info on the input data:
The input has a lines are as follows:
TimeStamp: 2014-07-16T18:40:48Z
2 lines later: a bunch of messages in one line and within each line, multiple times is:
sourceTimeStamp="2014-07-16T18:38:27Z"
If you have GNU's date (not busybox's), you can give difference in seconds with:
#!/bin/bash
A=$(date -d '2014-07-16T18:40:48Z' '+%s')
B=$(date -d '2014-07-16T18:38:27Z' '+%s')
echo "$(( A - B )) seconds"
For busybox's date and ash (modern probably / BusyBox v1.21.0):
#!/bin/ash
A=$(busybox date -d '2014-07-16 18:40:48' '+%s')
B=$(busybox date -d '2014-07-16 18:38:27' '+%s')
echo "$(( A - B )) seconds"
you should be able to use date like this (e.g.)
date +%s --date="2014-07-16T18:40:48Z"
to convert both timestamps into a unix timestamp. Getting the time difference between them is then reduced to a simple subtraction.
Does this help?
I would use awk. The following script searches for the lines of interest, converts the time value into a UNIX timestamp and saves them in the start, end variables. At the end of the script the difference will get calculated and printed:
timediff.awk:
/received time/ {
"date -d "$1" +%s" | getline end
}
/source time/ {
"date -d "$1" +%s" | getline start
exit
}
END {
printf "%s seconds in between", end - start
}
Execute it like this:
awk -f timediff.awk log.file
Output:
141 seconds in between

Generate time serie in iso-8601 format using date command, how to deal with server system date origin offset?

I have the following bash function that generate an epoch list in iso-8601 format on a machine that runs ubuntu and it works fine. (where isdate and isint bash functions to test the input)
gen_epoch()
{
## USAGE: gen_epoch [start_date_iso] [end_date_iso] [increment_in_seconds]
##
## TASK : generate an epoch list (epoch list in isodate format).
## result on STDOUT: [epoch_list]
## error_code : 2 0
## test argument
if [ "$#" -ne 3 ]; then echo "$FUNCNAME: input error [nb_of_input]"; return 2
elif [ $( isdate $1 &> /dev/null; echo $? ) -eq 2 ]; then echo "$FUNCNAME: argument error [$1]"; return 2
elif [ $( isdate $2 &> /dev/null; echo $? ) -eq 2 ]; then echo "$FUNCNAME: argument error [$2]"; return 2
elif [ $( isint $3 &> /dev/null; echo $? ) -eq 2 ]; then echo "$FUNCNAME: argument error [$3]"; return 2
else local beg=$( TZ=UTC date --date="$1" +%s ); local end=$( TZ=UTC date --date="$2" +%s ); local inc=$3; fi
## generate epoch
while [ $beg -le $end ]
do
local date_out=$( TZ=UTC date --date="UTC 1970-01-01 $beg secs" --iso-8601=seconds ); beg=$(( $beg + $inc ))
echo ${date_out%+*}
done
}
It generates the expected values for this command line example:
gen_epoch 2014-04-01T00:00:00 2014-04-01T07:00:00 3600
expected values:
2014-04-01T00:00:00
2014-04-01T01:00:00
2014-04-01T02:00:00
2014-04-01T03:00:00
2014-04-01T04:00:00
2014-04-01T05:00:00
2014-04-01T06:00:00
2014-04-01T07:00:00
However i have tried this function on a server where i have no root privileges and i have found the following results:
2014-03-31T17:00:00
2014-03-31T18:00:00
2014-03-31T19:00:00
2014-03-31T20:00:00
2014-03-31T21:00:00
2014-03-31T22:00:00
2014-03-31T23:00:00
2014-04-01T00:00:00
and i have seen that the server time origin is not at 1970-01-01T00:00:00.
typing TZ=UTC date --date="1970-01-01T00:00:00" +%s command gives the value of -25200 which corresponds to a 7 hours lag while it should give 0.
My question is how this problem could be corrected on the server?
Could You help me to find an equivalent solution for this function assuming that i don't know on which machine i am running it, so i have know apriori knowledge if the system time is correct or not?
Not a complete answer but too long for a comment.
I guess that this particular server is incorrectly configured upon setup. The problem is that BIOS clocks are set to localtime time, while the systems thinks it's in UTC (or vise versa) (use hwclock to query hardware clocks settings).
If the system is configured incorrectly and you can't fix it for any reason (don't have superuser account or whatever), I'd suggest to provide a "fixing timezone description file with your software and specify it in TZ variable like this: TZ=:/path/to/fixing/timezone date --date="1970-01-01T00:00:00" +%s. Obviously you have to pre-calculate which TZ description file fixes the problem and use a proper one. Usually available timezones are stored in /usr/share/zoneinfo

Resources