I’ve been spending more time than I would like writing shell scripts recently, as I spend more time configuring my setup than I do on ‘real’ projects. What I’ve found interesting is how simple the core of a shell is, and the tricks some commands do to build on this.
Most *nix users have probably had a moment were they were writing a shell script and forgotten the syntax for an if
statement. I write shell scripts so infrequently I often have to look it up. However all the if
statement does is run the condition command and check the exit status, if it is 0 it will run the main block, anything else and it runs the else block.
“But what about the square brackets?” I would think to myself. Well that’s just a command. You know, the [
command. man [
reveals that this is just a standard command with some flags to tell it what kind of thing to check.
Let’s take a simple conditional that checks that two numbers are equal:
if [ $num1 -eq $num2 ]; then
echo "Equal!"
fi
If num1
is 4
and num2
is 5
, the [
command will receive "4"
, "-eq"
, "5"
, and "]"
(remember everything is a string in the shell). The command takes the arguments up to the closing square bracket and does the comparison, in this case -eq
means integer comparison. As far as I can tell the closing bracket is just for readability - if you have a condition with with logical operators (||
or &&
) then each part of the expression can be in separate brackets (or you can use the -o
and -a
options to keep them in the same set of brackets).
So this means that we can do things like this:
[ -f some/file/path ] && cool_function_on_file some/file/path
Making use of the &&
builtin, rather than writing a whole if;then;fi
block. Or when we remember that the condition can be any command, we can be a bit smarter in scripts:
if git clone "$full_remote$user/$project.git" "$_path"; then
echo_cd $_path
fi
This will only change to the cloned repos directory if it cloned successfully (indicated by the result of the git
command).
for
loops work in a similar way, except instead of the condition we have a command that produces an output with each element separated by the $IFS
variable. The $IFS
is basically just whitespace/ newlines so we can capture the output of ls
and iterate through each filename:
for filename in $(ls); do
echo "It's a thing: $filename"
done
So in short, I have learnt a bit about shell scripting and now think it’s kind of neat rather than getting frustrated at the seemingly nonsensicle syntax.
For reference
[[
and==
are builtins to BASH and other newer shells.==
is no different to=
(but it can’t be used to accidentally assign something).[[
works the same as[
apart form the fact that it can be used with<
and>
for comparison, as it can process them before they are interpreted as IO redirection as part of a normal command.