The shell isn’t just the command line.
It’s also a programming language but one focused mostly on running other programs.
#!/bin/bash
echo hello world
The first line is called a “shebang” line, short for “sharp bang”. It tells the OS how to run this file, namely by passing it to /bin/bash
.
Because in bash #
is a comment character that line doesn’t have any affect when bash runs the file. Bash also ignores blank lines. Then the echo
line actually does something and then the program ends.
hello.sh
Create a file called hello.sh
and type the code from the previous slide into it.
The simplest way to run your script is:
bash hello.sh
Try it.
sudo apt install shellcheck
This is a fantastically good linter for bash.
You will write more robust scripts and also learn a lot about how bash actually works if you run it regularly.
At the terminal run:
chmod +x hello.sh
This makes the file “excutable” which tells the OS that it should run it if it is invoked like a program.
./hello.sh
#!/bin/bash
echo hello $1
$1
is a built in variable that gets the first argument passed on the command line.
Run this like:
./hello.sh foo
$ shellcheck hello.sh
In hello.sh line 4:
echo hello $1
^-- SC2086: Double quote to prevent globbing and word splitting.
Did you mean:
echo hello "$1"
For more information:
https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...
#!/bin/bash
echo "hello $1"
Now shellcheck hello.sh
should say nothing.
No news is good news.
#!/usr/bin/env bash
echo "hello $1"
env
is the same program we used before to dump out all the environment variables. Here we are using it to find and invoke bash
.
This lets us find whatever version of bash
the user has in their PATH
.
set -euo pipefail
Another line that’s it’s a good idea to add at the top of you scipt.
Sets some shell options that are generally more robust for scripts.
-e
Makes it so if a command invoked by the script fails then the script itself exits immediately. (Unless the command invocation is part of a control construct in which case it’s success or failure acts as a boolean.)
-u
Makes the script fail if it tries to reference an variable that has not been assigned a value, i.e. is undefined.
For instance if you write a line like rm -rf ~/$directory
in your script and forgot to set directory
, without -u
the script would delete your whole home directory because that would turn into rm -rf ~/
.
-o pipefail
Causes a pipeline to fail if any program in the pipeline fails.
Combined with -e
this will cause the script itself to exit if any part of a pipeline fails.
#!/usr/bin/env bash
set -euo pipefail
echo "hello $1"
if
#!/usr/bin/env bash
set -euo pipefail
day="$1"
file="$2"
if grep "$day" "$file"; then
echo "Found $day in $file"
else
echo "No $day found in $file"
fi
Invoking grep
either succeeds (the pattern is found at least once in the file) or fails (the pattern is not found or the file does not exist.)
If it succeeds that effectively “true” and failure is “false”.
if
#!/usr/bin/env bash
set -euo pipefail
day="$1"
file="$2"
if grep "$day" "$file" > /dev/null; then
echo "Found $day in $file"
else
echo "No $day found in $file"
fi
Redirecting output to /dev/null
throws it away.
for
#!/usr/bin/env bash
set -euo pipefail
shopt -s nullglob
for f in *.txt; do
cp "$f" "$f.bakup"
done
Loop over the files matched by the glob *.txt
and copy each one to a file with the same name plus the extension .backup
.
Note the shopt -s nullglob
.
[
and [[
#!/usr/bin/env bash
set -euo pipefail
if [[ -e config.txt ]]; then
echo "Have a config file."
fi
[[
is a bash
builtin but it’s based on a program called test
which also is installed under the name [
.
man test
to learn about the things you can test.
help [[
to find out about [[
and the things you can do with it that you can’t do with [
.
Recall that when you run a script it can’t change the current shell’s state.
There is, however, another way to run a script that does change the current shell:
source somescript.sh
or
. somescript.sh
This has the same effect as if you typed the commands in the file into the shell.