Well done! Wasn't too tricky in the end was it? Ha ha!
You have a couple of minor things incorrect. They might just be typos in your post.
The format specifiers for your date commands are a little off.
The date command expects the following type of syntax:
date [options] .... +[format]
So to get the week number:
And likewise, the format-specifier to get the name of the day would be
+%A
I do also have a few other minor comments, that are optional, stylistic things.
Firstly, I'd recommend using the more modern
$()
syntax for dealing with sub-shells/command substitutions, because they are POSIX compliant and are much more powerful and expressive than using back-ticks.
Back-ticks are considered deprecated and are only kept for backwards compatibility with older Bourne shell-scripts that might be on any given system.
One advantage of the
$()
syntax is -you can nest multiple substitutions. Which is tricky to do with back-ticks.
For example if you wanted to find out which directory contains the
ls
command, you could do this:
Bash:
echo "$(dirname $(which ls))"
In the above, the nested substitutions are quite easy to read and understand. And when they are ran, they run predictably too.
Whereas with back-ticks, things are a bit more fiddly:
e.g.
Bash:
echo `dirname \`which ls\``
Once you're dealing with nested back-tick expressions, the nested back-ticks have to be escaped with backslashes
\
. And you may have to escape certain other special characters too.
So you could experience a lot of problems trying to get the syntax correct with back-ticks.
And the more nested items you have, the more confusing the syntax looks.
Whereas the
$()
syntax is a lot cleaner, easier to understand and is less likely to cause bugs, or confusion. Also
$()
is POSIX compliant and NOT deprecated like back-ticks!
As things stand, in your script, you're only dealing with simple command substitutions. You aren't nesting anything, so the back-ticks in your script aren't going to cause a problem for you. But the information about using the
$()
syntax is worth bearing in mind going forward.
Also, in your echo statements, because you are using double quotes, your variables can be used directly inside the quotes.
e.g.
Instead of this:
Bash:
echo "this week number is: " $WEEK
You could do this:
Bash:
echo "This week number is: $WEEK"
When assigning values to string variables, especially via sub-shells/command substitutions - you should enclose them in double quotes. And likewise whenever you dereference/substitute a value from a string variable you should also double quote - it prevents various problems with globbing and problems with special characters that might otherwise need to be escaped.
So for example - if you used a command substitution to get a file-name from another command and it returned a file-name with a space - if you enclose the entire substitution with double quotes, the space in the file-name should not cause any problems. Whereas if you don't put the substitution inside double quotes - the shell would interpret the file-name as two file-names because the space in the file-name is not escaped. So that's another thing to bear in mind!
As abstract examples of that:
Lets say we're calling a script at
/path/to/getSomeFilename.sh
and it returns us a filename called
/path/to/some file.txt
This version uses no quotes:
Bash:
filename=$(/path/to/getSomeFilename.sh)
cp $filename /path/to/outputDirectory/
In the assignment, the filename will be assigned the string value
"/path/to/some file.txt"
Then in the cp command, because we failed to use quotes when we dereferenced/substituted the value, it expands to:
Bash:
cp /path/to/some file.txt /path/to/someOutputDirectory/
And because the filename has a literal space in it - the shell will think we're attempting to copy two files.
one called
/path/to/some
and one in the current directory called
file.txt
, which is not at all correct, or what we intended. So we'd probably get two errors about files that could not be found.
Whereas if we use quotes - everything works as we'd expect:
Bash:
filename="$(/path/to/getSomeFilename.sh)"
cp "$filename" /path/to/outputDirectory/
Because we've double quoted everything - when we do the substitution in the final
cp
command, it will expand to this:
Bash:
cp "/path/to/some file.txt" /path/to/outputdirectory
Because the path for the entire filename is in double quotes, the space is automatically escaped and our file is copied.
So it's good practice to double quote when we assign values to string variables.
And it's good practice to double quote when we dereference/substitute values from variables.
Anyway - getting back on-topic:
Here's a cleaned up version of your script with the typo's removed and using more modern, POSIX compliant substitutions:
Bash:
#!/bin/bash
WEEK="$(date +%V)"
DAY="$(date +%A)"
echo "This week number is: $WEEK"
echo "Today's day is: $DAY"
if [[ ($(($WEEK%2)) -eq 0) && ("$DAY" = "Saturday") ]]
then
echo "the admin can perform their task"
else
echo "it's not the time for the task to be performed yet"
fi
Oh - and here’s my version of your script as a bash one-liner:
Bash:
echo -n "It is "; [ "$(date +%A)" == "Saturday" ] && [ $(($(date +%W)%2)) -eq 0 ] && echo -n "time " || echo -n "NOT time "; echo "for the admin to perform their task!"
Though - if you want to get technical about it - it's kinda three lines munged into one....
If I separate the three lines, you might be able to see more clearly what I've done:
Bash:
echo -n "It is ";
[ "$(date +%A)" == "Saturday" ] && [ $(($(date +%W)%2)) -eq 0 ] && echo -n "time " || echo -n "NOT time ";
echo "for the admin to perform their task!"
Line 2 of the above uses some slightly alternate syntax for
if..then..else..
, which just uses boolean/logical AND and OR operations.
In that line - We've used four distinct operations, joined together using logical
&&
(AND) and
||
(OR) operators.
So it's structure is:
[ condition1 ] && [ condition2 ] && echo "time " || echo "NOT time"
Because we've used
&&
between the first and second operations:
If condition1 (day is saturday) evaluates to true, then condition 2 (week is even) is evaluated.
If condition2 evaluates to true, then because the third operation follows an
&&
- in the third operation we echo the string
"time "
.
If either of condition1 or condition2 evaluates to false, then the fourth operation (after the
||
) is ran and we echo
"NOT time"
to the screen.
So that single line alone contains an entire
if..then..else..
To prove it - I'll change code for the entire "one-liner" to use more traditional
if...then...else..
syntax.
So the original one-liner I posted becomes this:
Bash:
echo -n "It is ";
if [ "$(date +%A)" == "Saturday" ] && [ $(($(date +%W)%2)) -eq 0 ]
then
echo -n "time "
else
echo -n "not time "
fi
echo "for the admin to perform their task!"
So, effectively - in my bash "one-liner", I've condensed 8 lines of code, into 3 lines of code - on a single line.
That type of "condensed" syntax, using logical operators comes in handy when you want to do something quickly, using simple
if..then..else..
type logic, and without having to write a multi-line script. It allows you to put a lot of functionality into a single line. But it can be a little more confusing for people to read and understand. But it's another technique that can be useful to know from time to time!
Personally, I only use it in one-liners. Or if I'm feeling lazy and I only need to use simple
if..then..else..
type logic. Anything requiring more complicated branching logic gets turned into a script!
Sorry about the wall of text. It's a bit of a bootcamp, but hopefully you, or someone else will find it useful!