A `bash` script is a sequence of command that are executed one by one.
Most of the time we just execute one command and wait for the result to then make a decision and execute an other command.
We can however create a sequence of commands on the command line.
```
(baseline-) ➜ ~ echo hello world
hello world
(baseline-) ➜ ~ date
Wed 16 Mar 2022 07:06:02 PM CET
(baseline-) ➜ ~ cal
March 2022
Su Mo Tu We Th Fr Sa
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
(baseline-) ➜ ~ echo hello world date cal
hello world date cal
(baseline-) ➜ ~ echo hello world; date; cal
hello world
Wed 16 Mar 2022 07:06:19 PM CET
March 2022
Su Mo Tu We Th Fr Sa
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
```
A very visually similar result, but completely different operation, can be obtained by replacing the `;` with `&` or `&&`.
`&` will launch a new process and send it to the background, `&&` will **evaluate** the return status of your process and only continue **if** the status was `0`, or in other words successful.
```
(baseline-) ➜ ~ echo hello world & date & cal
[1] 3075524
hello world
[2] 3075525
[1] - 3075524 done echo hello world
Wed 16 Mar 2022 07:06:33 PM CET
[2] + 3075525 done date
March 2022
Su Mo Tu We Th Fr Sa
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
(baseline-) ➜ ~ echo hello world && date && cal
hello world
Wed 16 Mar 2022 07:06:40 PM CET
March 2022
Su Mo Tu We Th Fr Sa
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
(baseline-) ➜ ~
```
This should make you think we can create quite complicated logical flows in `bash` and you're right!
But first things first, let's create our first script!
```
(baseline-) ➜ bash file test.sh
test.sh: Bourne-Again shell script, ASCII text executable
(baseline-) ➜ bash ls -l test.sh
-rwxr-xr-x 1 waldek waldek 39 Mar 16 19:13 test.sh
(baseline-) ➜ bash cat test.sh
#!/bin/bash
echo hello world
cal
date
(baseline-) ➜ bash ./test.sh
hello world
March 2022
Su Mo Tu We Th Fr Sa
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Wed 16 Mar 2022 07:27:22 PM CET
(baseline-) ➜ bash bash test.sh
hello world
March 2022
Su Mo Tu We Th Fr Sa
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Wed 16 Mar 2022 07:27:25 PM CET
(baseline-) ➜ bash
```
If you observe the output above you can conclude multiple things.
* the `test.sh` file is a simple text file
* it has executable permissions for the owner, group and others
* we can execute the sequence of commands in the script in two ways
*`./test.sh`
*`bash test.sh`
* the content is a series of commands but with a *weird* first line
That first line is called a [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) and is a way of explaining which interpreter understands the lines that follow.
If you venture out into the python universe you'll encounter the same norm but with a different path to an interpretor, often `/bin/python3` or `/usr/bin/env python3`.
The later is a sort of shortcut that points to the local python installation, even if it's not in a standard location.
A shebang is not necessary for a script to function but is **highly** advised.
The small *password* checker we made before could use some [conditional logic](https://en.wikipedia.org/wiki/Conditional_(computer_programming)).
We can easily implement this in `bash` with the following syntax.
```bash
#!/bin/bash
my_pass="supersecret"
read -s -p "what is your secret password? " pass
echo
if [ $pass == $my_pass ]; then
echo "access granted!"
fi
```
The `[ $pass == $my_pass ]` is the actual *evaluation* and will **always** evaluate to either `true` or `false`.
If the statement is `true`, the following code get's executed.
If **not**, currently nothing happens.
We can introduce a second keyword `else` to handle this.
```bash
#!/bin/bash
my_pass="supersecret"
read -s -p "what is your secret password? " pass
echo
if [ $pass == $my_pass ]; then
echo "access granted!"
else
echo "access denied..."
fi
```
The script above gives us the following behaviour.
```
waldek@helloworld:~$ bash test.sh
what is your secret password?
access denied...
waldek@helloworld:~$ bash test.sh
what is your secret password?
access granted!
waldek@helloworld:~$
```
We can add a bit more complexity to our possible branches with the `elif` keyword.
This keyword allows us to construct a second and third branch of execution.
Consider the sentence below.
> If you are younger than 27 you are still young so if you're older than 27 you're considered old, but if you are 27 on the dot your life might be at risk!
This sentence can be converted to a conditional logic block as follows.
```bash
#!/bin/bash
read -p "how old are you? " age
echo
if [ "$age" -lt "27" ]; then
echo "you are so young! enjoy it"
elif [ "$age" -gt "27" ]; then
echo "you're sooo old!"
elif [ "$age" -eq "27" ]; then
echo "your life might be at risk..."
else
echo "I'm not sure I understand you."
fi
```
There is a little problem here though!
We can input anything we want, not only numbers, and this creates some error messages.
Bash is a bit special, compared to a language like `python3`, because it doesn't *crash* on an error.
It just *keeps going*.
```
waldek@helloworld:~$ bash test.sh
how old are you? helloworld
test.sh: line 6: [: helloworld: integer expression expected
test.sh: line 8: [: helloworld: integer expression expected
test.sh: line 10: [: helloworld: integer expression expected
I'm not sure I understand you.
waldek@helloworld:~$
```
We can check if the input is *really* a number and redirect the error to `/dev/null`.
If the number is not a real number we can't continue so we `exit` the script.
```bash
#!/bin/bash
read -p "how old are you? " age
echo
if ! [ "$age" -eq "$age" ] 2> /dev/null
then
echo "Sorry integers only"
exit 1
fi
if [ "$age" -lt "27" ]; then
echo "you are so young! enjoy it"
elif [ "$age" -gt "27" ]; then
echo "you're sooo old!"
elif [ "$age" -eq "27" ]; then
echo "your life might be at risk..."
fi
```
## How does it work behind the scenes?
TODO - explain `test` and exit status
The table below is taken from the bash reference manual you can find [here](https://www.gnu.org/software/bash/manual/bash.html#Bash-Conditional-Expressions).
| <divstyle="width:300px">command</div> | status |
| --- | --- |
| -a file | True if file exists. |
| -b file | True if file exists and is a block special file. |
| -c file | True if file exists and is a character special file. |
| -d file | True if file exists and is a directory. |
| -e file | True if file exists. |
| -f file | True if file exists and is a regular file. |
| -g file | True if file exists and its set-group-id bit is set. |
| -h file | True if file exists and is a symbolic link. |
| -k file | True if file exists and its "sticky" bit is set. |
| -p file | True if file exists and is a named pipe (FIFO). |
| -r file | True if file exists and is readable. |
| -s file | True if file exists and has a size greater than zero. |
| -t fd | True if file descriptor fd is open and refers to a terminal. |
| -u file | True if file exists and its set-user-id bit is set. |
| -w file | True if file exists and is writable. |
| -x file | True if file exists and is executable. |
| -G file | True if file exists and is owned by the effective group id. |
| -L file | True if file exists and is a symbolic link. |
| -N file | True if file exists and has been modified since it was last read. |
| -O file | True if file exists and is owned by the effective user id. |
| -S file | True if file exists and is a socket. |
| file1 -ef file2 | True if file1 and file2 refer to the same device and inode numbers. |
| file1 -nt file2 | True if file1 is newer (according to modification date) than file2, or if file1 exists and file2 does not. |
| file1 -ot file2 | True if file1 is older than file2, or if file2 exists and file1 does not. |
| -o optname | True if the shell option optname is enabled. The list of options appears in the description of the -o option to the set builtin (see The Set Builtin). |
| -v varname | True if the shell variable varname is set (has been assigned a value). |
| -R varname | True if the shell variable varname is set and is a name reference. |
| -z string | True if the length of string is zero. |
| -n string string | True if the length of string is non-zero. |
| string1 == string2 | True if the strings are equal. When used with the [[ command, this performs pattern matching as described above (see Conditional Constructs). ‘=’ should be used with the test command for POSIX conformance. |
| string1 = string2 | True if the strings are equal. When used with the [[ command, this performs pattern matching as described above (see Conditional Constructs). ‘=’ should be used with the test command for POSIX conformance. |
| string1 != string2 | True if the strings are not equal. |
[This](https://gitea.86thumbs.net/waldek/python_course_doc) repository has a twenty day course to learn python written by me.
The main file you need to follow is [this](https://gitea.86thumbs.net/waldek/python_course_doc/src/branch/master/learning_python3.md) one.
Some practical exercises can be found [here](https://gitea.86thumbs.net/waldek/linux_course_doc/src/branch/master/modules/qualifying/exercise_python.md) together with the needed [source files](https://gitea.86thumbs.net/waldek/linux_course_doc/src/branch/master/modules/qualifying/assets/files.tar.gz).
## Vim as an IDE
I made a tutorial on the essentials of [vim customization](https://gitea.86thumbs.net/waldek/linux_course_doc/src/branch/master/modules/qualifying/learning_vim_configuration.md).
My real world configuration can be found at [this](https://gitea.86thumbs.net/waldek/vimrc) repository.