some progress...
This commit is contained in:
parent
856bab763d
commit
4f9e0ccf3d
405
CCNA/readme.md
405
CCNA/readme.md
|
@ -1,6 +1,4 @@
|
||||||
# Scripting
|
# bash
|
||||||
|
|
||||||
## Bash
|
|
||||||
|
|
||||||
A `bash` script is a sequence of command that are executed one by one.
|
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.
|
Most of the time we just execute one command and wait for the result to then make a decision and execute an other command.
|
||||||
|
@ -274,6 +272,7 @@ waldek@helloworld:~$
|
||||||
|
|
||||||
If you open up a new shell this `$name` variable will not be defined because variables are local to each instance of `bash` that is running.
|
If you open up a new shell this `$name` variable will not be defined because variables are local to each instance of `bash` that is running.
|
||||||
This can be observed as follows.
|
This can be observed as follows.
|
||||||
|
We can *export* variables to children with the `export` keyword.
|
||||||
|
|
||||||
```
|
```
|
||||||
waldek@helloworld:~$ name="wouter gordts"
|
waldek@helloworld:~$ name="wouter gordts"
|
||||||
|
@ -293,7 +292,7 @@ exit
|
||||||
waldek@helloworld:~$
|
waldek@helloworld:~$
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using variables to store the output of command
|
# Using variables to store the output of command
|
||||||
|
|
||||||
Bash only really knows *characters*, both for sending and receiving.
|
Bash only really knows *characters*, both for sending and receiving.
|
||||||
We can temporarily store the output of a command using variables.
|
We can temporarily store the output of a command using variables.
|
||||||
|
@ -310,7 +309,7 @@ waldek:x:1000:1000:waldek,,,:/home/local/waldek:/bin/zsh
|
||||||
waldek@metal:~$
|
waldek@metal:~$
|
||||||
```
|
```
|
||||||
|
|
||||||
A little but more complicated.
|
A little bit more complicated.
|
||||||
|
|
||||||
```
|
```
|
||||||
waldek@metal:~$ count=$(grep "/home" /etc/passwd | wc -l)
|
waldek@metal:~$ count=$(grep "/home" /etc/passwd | wc -l)
|
||||||
|
@ -350,9 +349,29 @@ it is 02:17:02 PM and we are a Tuesday in 2022
|
||||||
waldek@metal:~$
|
waldek@metal:~$
|
||||||
```
|
```
|
||||||
|
|
||||||
### Getting input into the script
|
# Coding challenge - Output system stats
|
||||||
|
|
||||||
#### With `read`
|
|
||||||
|
Write a program that prints information about your computer such as:
|
||||||
|
|
||||||
|
* the hostname
|
||||||
|
* the FQDN
|
||||||
|
* number of cpus
|
||||||
|
* type of cpu
|
||||||
|
* amount of RAM
|
||||||
|
|
||||||
|
Write a program that prints information about a given user such as:
|
||||||
|
|
||||||
|
* name
|
||||||
|
* UID
|
||||||
|
* their default shell
|
||||||
|
* groups they are a member of
|
||||||
|
* number of files in their home directory
|
||||||
|
* amount of disk space they use
|
||||||
|
|
||||||
|
# Getting input into the script
|
||||||
|
|
||||||
|
## With `read`
|
||||||
|
|
||||||
Observe the output of the following *program*.
|
Observe the output of the following *program*.
|
||||||
It's not really complicated but it will demonstrate we can do arithmetic in bash scripts as well!
|
It's not really complicated but it will demonstrate we can do arithmetic in bash scripts as well!
|
||||||
|
@ -379,7 +398,40 @@ echo "your are probably around $(( $this_year - $year ))..."
|
||||||
waldek@metal:~$
|
waldek@metal:~$
|
||||||
```
|
```
|
||||||
|
|
||||||
#### With command line arguments
|
# Coding challenge - Secret input
|
||||||
|
|
||||||
|
Can you create me a secret password prompt?
|
||||||
|
Something like this.
|
||||||
|
|
||||||
|
```
|
||||||
|
waldek@metal:~$ bash test.sh
|
||||||
|
what is your secret password?
|
||||||
|
hmmm, I don't know how to compare helloworld to supersecret
|
||||||
|
waldek@metal:~$
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Spoiler warning!</summary>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
my_pass="supersecret"
|
||||||
|
|
||||||
|
read -s -p "what is your secret password? " pass
|
||||||
|
echo
|
||||||
|
echo "hmmm, I don't know how to compare $pass to $my_pass"
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### `read` multiple variables
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
## With command line arguments
|
||||||
|
|
||||||
We can create a similar behaviour but with command line arguments.
|
We can create a similar behaviour but with command line arguments.
|
||||||
By doing so we don't have to answer any questions the script poses at runtime.
|
By doing so we don't have to answer any questions the script poses at runtime.
|
||||||
|
@ -405,6 +457,14 @@ This variable represents the *first* argument on the command line.
|
||||||
Knowing this, what would `$4` mean?
|
Knowing this, what would `$4` mean?
|
||||||
Indeed, the *fourth* argument...
|
Indeed, the *fourth* argument...
|
||||||
|
|
||||||
|
## From a file
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
## From a pipe
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
# Coding Challenge - output the exact output below
|
# Coding Challenge - output the exact output below
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -428,59 +488,334 @@ echo "here are all of them on one line: $@"
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
# More math!
|
||||||
|
|
||||||
|
## The `let` keyword
|
||||||
|
|
||||||
### What is a Bash Script - What are they, how do they work and how to run them.
|
TODO
|
||||||
|
|
||||||
[Ryan's tutorials](https://ryanstutorials.net/bash-scripting-tutorial/bash-script.php)
|
## The `expr` keyword
|
||||||
|
|
||||||
### Variables - Store data temporarily for later use.
|
TODO
|
||||||
|
|
||||||
[Ryan's tutorials](https://ryanstutorials.net/bash-scripting-tutorial/bash-variables.php)
|
## Double parenthesis
|
||||||
|
|
||||||
Write a program that prints information about your computer such as:
|
We've already seen the basic syntax before but here are some more examples.
|
||||||
|
|
||||||
* the hostname
|
```
|
||||||
* the FQDN
|
waldek@metal:~$ a=11
|
||||||
* number of cpus
|
waldek@metal:~$ b=202
|
||||||
* type of cpu
|
waldek@metal:~$ echo $(( $a + $b ))
|
||||||
* amount of RAM
|
213
|
||||||
|
waldek@metal:~$ echo $(( $a - $b ))
|
||||||
|
-191
|
||||||
|
waldek@metal:~$ echo $(( $a * $b ))
|
||||||
|
2222
|
||||||
|
waldek@metal:~$ echo $(( $a / $b ))
|
||||||
|
0
|
||||||
|
waldek@metal:~$ echo $(( $a % $b ))
|
||||||
|
11
|
||||||
|
```
|
||||||
|
|
||||||
Write a program that prints information about a given user such as:
|
Incrementing variables can also be done with the double parenthesis syntax.
|
||||||
|
We can't use the `$` to reference the variable though.
|
||||||
|
This is a *classic* example of bash's finicky behaviour.
|
||||||
|
|
||||||
* name
|
```
|
||||||
* UID
|
waldek@metal:~$ echo $(( b++ ))
|
||||||
* their default shell
|
202
|
||||||
* groups they are a member of
|
waldek@metal:~$ echo $(( b++ ))
|
||||||
* number of files in their home directory
|
203
|
||||||
* amount of disk space they use
|
waldek@metal:~$ echo $(( b++ ))
|
||||||
|
204
|
||||||
|
waldek@metal:~$ echo $(( b++ ))
|
||||||
|
205
|
||||||
|
waldek@metal:~$ echo $(( b++ ))
|
||||||
|
206
|
||||||
|
waldek@metal:~$
|
||||||
|
```
|
||||||
|
|
||||||
### Input - Different ways to supply data and directions to your Bash script.
|
## Variable length
|
||||||
|
|
||||||
[Ryan's tutorials](https://ryanstutorials.net/bash-scripting-tutorial/bash-input.php)
|
As bash only *knows* characters it has a built in feature to determine a variable's length.
|
||||||
|
You can print the length of a variable, or use it to calculate something, with the following syntax.
|
||||||
|
|
||||||
### Arithmetic - Perform various arithmetic operations in your Bash script.
|
```
|
||||||
|
waldek@metal:~$ test="hello world! bash is pretty sweet..."
|
||||||
|
waldek@metal:~$ echo ${#test}
|
||||||
|
36
|
||||||
|
waldek@metal:~$ echo $(( ${#test} + 1986 ))
|
||||||
|
2022
|
||||||
|
waldek@metal:~$
|
||||||
|
```
|
||||||
|
|
||||||
[Ryan's tutorials](https://ryanstutorials.net/bash-scripting-tutorial/bash-arithmitic.php)
|
[Ryan's tutorials](https://ryanstutorials.net/bash-scripting-tutorial/bash-arithmitic.php)
|
||||||
|
|
||||||
### If Statements - How to make decisions within your Bash script.
|
# If Statements - How to make decisions within your Bash script.
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
| <div style="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. |
|
||||||
|
| string1 < string2 | True if string1 sorts before string2 lexicographically. |
|
||||||
|
| string1 > string2 | True if string1 sorts after string2 lexicographically. |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Nested `if` statements
|
||||||
|
|
||||||
|
It's worth pointing out we can *nest* `if` statements *inside* other `if` statements.
|
||||||
|
There is no real theoretical limit to how *deep* we can go, but it's advised to keep the limit to two or three levels.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
num="40"
|
||||||
|
|
||||||
|
if [ "$num" -lt "300" ]; then
|
||||||
|
echo "$num is a small number"
|
||||||
|
if [ "$(( $num % 2))" -eq 0 ]; then
|
||||||
|
echo "and it is even"
|
||||||
|
else
|
||||||
|
echo "$num is not even"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
# Coding challenge - File information
|
||||||
|
|
||||||
|
Write a script that takes one argument which should be a valid file path.
|
||||||
|
The program should print out what type of file this is, and if it is readable, print the first and last 5 lines.
|
||||||
|
If the file does not exist, an error message should be shown.
|
||||||
|
Something along these lines.
|
||||||
|
|
||||||
|
```
|
||||||
|
waldek@helloworld:~$ bash test.sh .bashrc
|
||||||
|
.bashrc exists, I'll dig a little deeper
|
||||||
|
it is indeed a file
|
||||||
|
and I can read it!
|
||||||
|
here are the first 5 lines
|
||||||
|
# ~/.bashrc: executed by bash(1) for non-login shells.
|
||||||
|
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
|
||||||
|
# for examples
|
||||||
|
|
||||||
|
# If not running interactively, don't do anything
|
||||||
|
and here are the last 5 lines
|
||||||
|
elif [ -f /etc/bash_completion ]; then
|
||||||
|
. /etc/bash_completion
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
. "$HOME/.cargo/env"
|
||||||
|
waldek@helloworld:~$ bash test.sh .not_a_file
|
||||||
|
that's not a file!
|
||||||
|
waldek@helloworld:~$
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Spoiler warning!</summary>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
filepath=$1
|
||||||
|
|
||||||
|
if [ -e "$filepath" ]; then
|
||||||
|
echo "$filepath exists, I'll dig a little deeper"
|
||||||
|
if [ -f "$filepath" ]; then
|
||||||
|
echo "it is indeed a file"
|
||||||
|
if [ -r "$filepath" ]; then
|
||||||
|
echo "and I can read it!"
|
||||||
|
echo "here are the first 5 lines"
|
||||||
|
head -n 5 $filepath
|
||||||
|
echo "and here are the last 5 lines"
|
||||||
|
tail -n 5 $filepath
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "that's not a file!"
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## A *modern* version of `test`
|
||||||
|
|
||||||
|
### `[[ ]]`
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
### `(( ))`
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
### `&&` and `||`
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
[Ryan's tutorials](https://ryanstutorials.net/bash-scripting-tutorial/bash-if-statements.php)
|
[Ryan's tutorials](https://ryanstutorials.net/bash-scripting-tutorial/bash-if-statements.php)
|
||||||
|
|
||||||
Write a script that takes one argument which is a filepath.
|
# Loops - A variety of ways to perform repetitive tasks.
|
||||||
The program should print out what type of file this is, and if it is readable, print the first and last 5 lines.
|
|
||||||
|
|
||||||
### Loops - A variety of ways to perform repetitive tasks.
|
|
||||||
|
|
||||||
[Ryan's tutorials](https://ryanstutorials.net/bash-scripting-tutorial/bash-loops.php)
|
[Ryan's tutorials](https://ryanstutorials.net/bash-scripting-tutorial/bash-loops.php)
|
||||||
|
|
||||||
Write a script that sets all you cpu's to a desired governor.
|
Write a script that sets all you cpu's to a desired governor.
|
||||||
|
|
||||||
### Functions - Reuse code to make life easier.
|
Rename all files in a folder with an prefix or postfix.
|
||||||
|
|
||||||
|
# Functions - Reuse code to make life easier.
|
||||||
|
|
||||||
[Ryan's tutorials](https://ryanstutorials.net/bash-scripting-tutorial/bash-functions.php)
|
[Ryan's tutorials](https://ryanstutorials.net/bash-scripting-tutorial/bash-functions.php)
|
||||||
|
|
||||||
### User Interface - Make your scripts user friendly.
|
# User Interface - Make your scripts user friendly.
|
||||||
|
|
||||||
[Ryan's tutorials](https://ryanstutorials.net/bash-scripting-tutorial/bash-user-interfaces.php)
|
[Ryan's tutorials](https://ryanstutorials.net/bash-scripting-tutorial/bash-user-interfaces.php)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue