> ## Documentation Index
> Fetch the complete documentation index at: https://hackbook.dudji.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Bash Scripting

> Compact Bash fundamentals for arguments, variables, flow control, operators, functions, and debugging.

## Basic Script

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash

if [ $# -eq 0 ]
then
    echo -e "You need to specify the target domain.\n"
    echo -e "Usage:"
    echo -e "\t$0 <domain>"
    exit 1
else
    domain=$1
fi

<SNIP>
```

* `#!/bin/bash` selects the interpreter.
* `if-else-fi` handles conditional execution.
* `echo` prints output.
* `$#`, `$0`, and `$1` are special variables.
* `domain=$1` stores the first user argument.

## Shebang

The shebang is the first line of a script and starts with `#!`. It tells the
system which interpreter should run the file.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash
```

Other common examples:

```python theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/usr/bin/env python
```

```perl theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/usr/bin/env perl
```

## Conditionals

Use `if`, `elif`, `else`, and `fi` to branch based on conditions.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash

value=$1

if [ "$value" -gt "10" ]
then
    echo "Given argument is greater than 10."
elif [ "$value" -lt "10" ]
then
    echo "Given argument is less than 10."
else
    echo "Given argument is not a number."
fi
```

```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ bash if-elif-else.sh HTB
if-elif-else.sh: line 5: [: HTB: integer expression expected
if-elif-else.sh: line 8: [: HTB: integer expression expected
Given argument is not a number.
```

## Arguments and Variables

Bash supports positional arguments directly.

* `$0` is the script name.
* `$1` to `$9` are the first nine positional arguments.
* `$#` is the number of arguments.

```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ ./script.sh ARG1 ARG2 ARG3 ... ARG9

ASSIGNMENTS:
$0      $1   $2   $3 ...   $9
```

### Special Variables

Special variables use the [Internal Field Separator](https://bash.cyberciti.biz/guide/\$IFS)
(`IFS`) to split arguments.

| Special Variable | Description                                         |
| ---------------- | --------------------------------------------------- |
| `$#`             | Number of arguments passed to the script.           |
| `$@`             | Full list of command-line arguments.                |
| `$n`             | Argument by position, for example `$1`.             |
| `$$`             | PID of the current process.                         |
| `$?`             | Exit status of the last command. `0` means success. |

### Variables

Assignments do not use `$`, and there must be no spaces around `=`.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
domain=$1
echo "$domain"
```

Bash treats variable content mostly as strings unless used in arithmetic.
Variables are global by default unless declared with `local`.

```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ variable = "this will result with an error."
command not found: variable

Dudji@htb[/htb]$ variable="Declared without an error."
Dudji@htb[/htb]$ echo $variable
Declared without an error.
```

### Arrays

Arrays store multiple values under one variable name, indexed from `0`.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash

domains=(www.inlanefreight.com ftp.inlanefreight.com vpn.inlanefreight.com www2.inlanefreight.com)
echo ${domains[0]}
```

Quoted values stay together as one array element:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash

domains=("www.inlanefreight.com ftp.inlanefreight.com vpn.inlanefreight.com" www2.inlanefreight.com)
echo ${domains[0]}
```

## Arithmetic

Use arithmetic expansion `$((...))` and increment/decrement operators for math.

| Operator     | Description    |
| ------------ | -------------- |
| `+`          | Addition       |
| `-`          | Subtraction    |
| `*`          | Multiplication |
| `/`          | Division       |
| `%`          | Modulus        |
| `variable++` | Increase by 1  |
| `variable--` | Decrease by 1  |

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash

increase=1
decrease=1

echo "Addition: 10 + 10 = $((10 + 10))"
echo "Subtraction: 10 - 10 = $((10 - 10))"
echo "Multiplication: 10 * 10 = $((10 * 10))"
echo "Division: 10 / 10 = $((10 / 10))"
echo "Modulus: 10 % 4 = $((10 % 4))"

((increase++))
echo "Increase Variable: $increase"

((decrease--))
echo "Decrease Variable: $decrease"
```

`${#variable}` returns string length:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash

htb="HackTheBox"
echo ${#htb}
```

## Input and Output

### Input Control

Use `read` when the script should pause for user input.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
echo -e "Additional options available:"
echo -e "\t1) Identify the corresponding network range of target domain."
echo -e "\t2) Ping discovered hosts."
echo -e "\t3) All checks."
echo -e "\t*) Exit.\n"

read -p "Select your option: " opt
```

### Output Control

Use `tee` when you want to both display output and save it to a file.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
netrange=$(whois $ip | grep "NetRange\|CIDR" | tee -a CIDR.txt)
hosts=$(host $domain | grep "has address" | cut -d" " -f4 | tee discovered_hosts.txt)
```

`-a` appends instead of overwriting.

```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ cat discovered_hosts.txt CIDR.txt

165.22.119.202
NetRange:       165.22.0.0 - 165.22.255.255
CIDR:           165.22.0.0/16
```

## Flow Control - Branches

Branches let the script choose one path over another. In Bash, the main branch
constructs are `if-else` and `case`.

### Case Statements

`case` compares one expression against exact patterns.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
case <expression> in
    pattern_1 ) statements ;;
    pattern_2 ) statements ;;
    pattern_3 ) statements ;;
esac
```

Example:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
case $opt in
    "1") network_range ;;
    "2") ping_host ;;
    "3") network_range && ping_host ;;
    "*") exit 0 ;;
esac
```

## Functions

Functions keep scripts shorter and easier to reuse. Define them before the
first call because Bash reads from top to bottom.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
function name {
    <commands>
}
```

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
name() {
    <commands>
}
```

Example:

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
function network_range {
    for ip in $ipaddr
    do
        netrange=$(whois $ip | grep "NetRange\|CIDR" | tee -a CIDR.txt)
        cidr=$(whois $ip | grep "CIDR" | awk '{print $2}')
        cidr_ips=$(prips $cidr)
        echo -e "\nNetRange for $ip:"
        echo -e "$netrange"
    done
}
```

### Parameter Passing

Functions use their own positional parameters, just like scripts.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash

function print_pars {
    echo $1 $2 $3
}

one="First parameter"
two="Second parameter"
three="Third parameter"

print_pars "$one" "$two" "$three"
```

### Return Values

Functions return status codes, and `$?` reads the last one.

| Return Code | Description                    |
| ----------- | ------------------------------ |
| `1`         | General errors                 |
| `2`         | Misuse of shell builtins       |
| `126`       | Command invoked cannot execute |
| `127`       | Command not found              |
| `128`       | Invalid argument to exit       |
| `128+n`     | Fatal error signal `n`         |
| `130`       | Script terminated by Control-C |
| `255`       | Exit status out of range       |

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash

function given_args {
    if [ $# -lt 1 ]
    then
        echo -e "Number of arguments: $#"
        return 1
    else
        echo -e "Number of arguments: $#"
        return 0
    fi
}

given_args
echo -e "Function status code: $?\n"

given_args "argument"
echo -e "Function status code: $?\n"

content=$(given_args "argument")
echo -e "Content of the variable: \n\t$content"
```

## Flow Control - Loops

Loops repeat work until input is exhausted or a condition changes.

* `for` loops iterate over items.
* `while` loops run while a condition is true.
* `until` loops run while a condition is false.

### For Loops

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
for variable in 1 2 3 4
do
    echo $variable
done
```

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
for variable in file1 file2 file3
do
    echo $variable
done
```

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
for ip in 10.10.10.170 10.10.10.174
do
    ping -c 1 $ip
done
```

One-line form:

```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ for ip in 10.10.10.170 10.10.10.174;do ping -c 1 $ip;done
```

### While Loops

`while` loops need a counter or changing condition so they do not run forever.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
stat=1
while [ $stat -eq 1 ]
do
    ping -c 2 $host > /dev/null 2>&1
    if [ $? -eq 0 ]
    then
        echo "$host is up."
        ((stat--))
        ((hosts_up++))
        ((hosts_total++))
    else
        echo "$host is down."
        ((stat--))
        ((hosts_total++))
    fi
done
```

`continue` skips to the next iteration and `break` exits the loop.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash

counter=0

while [ $counter -lt 10 ]
do
    ((counter++))
    echo "Counter: $counter"

    if [ $counter == 2 ]
    then
        continue
    elif [ $counter == 4 ]
    then
        break
    fi
done
```

### Until Loops

`until` is the inverse of `while`: it runs until the condition becomes true.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
#!/bin/bash

counter=0

until [ $counter -eq 10 ]
do
    ((counter++))
    echo "Counter: $counter"
done
```

## Comparison Operators

Bash operators are usually grouped into string, integer, file, and logical
checks.

### String Operators

| Operator | Description                    |
| -------- | ------------------------------ |
| `==`     | is equal to                    |
| `!=`     | is not equal to                |
| `<`      | is less than in ASCII order    |
| `>`      | is greater than in ASCII order |
| `-z`     | string is empty                |
| `-n`     | string is not empty            |

Quote strings like `"$1"` to avoid parsing issues.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
if [ "$1" != "HackTheBox" ]
then
    echo -e "You need to give 'HackTheBox' as argument."
    exit 1
elif [ $# -gt 1 ]
then
    echo -e "Too many arguments given."
    exit 1
else
    echo -e "Success!"
fi
```

`<` and `>` string comparisons work inside `[[ ... ]]`.

```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ man ascii
```

### Integer Operators

| Operator | Description                 |
| -------- | --------------------------- |
| `-eq`    | is equal to                 |
| `-ne`    | is not equal to             |
| `-lt`    | is less than                |
| `-le`    | is less than or equal to    |
| `-gt`    | is greater than             |
| `-ge`    | is greater than or equal to |

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
if [ $# -lt 1 ]
then
    echo -e "Number of given arguments is less than 1"
    exit 1
elif [ $# -gt 1 ]
then
    echo -e "Number of given arguments is greater than 1"
    exit 1
else
    echo -e "Number of given arguments equals 1"
fi
```

### File Operators

| Operator | Description                |
| -------- | -------------------------- |
| `-e`     | file exists                |
| `-f`     | regular file               |
| `-d`     | directory                  |
| `-L`     | symbolic link              |
| `-N`     | modified since last read   |
| `-O`     | owned by current user      |
| `-G`     | group matches current user |
| `-s`     | size greater than 0        |
| `-r`     | readable                   |
| `-w`     | writable                   |
| `-x`     | executable                 |

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
if [ -e "$1" ]
then
    echo -e "The file exists."
    exit 0
else
    echo -e "The file does not exist."
    exit 2
fi
```

### Boolean and Logical Operators

Use `[[ ... ]]` for boolean-style checks.

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
if [[ -z $1 ]]
then
    echo -e "Boolean value: True (is null)"
    exit 1
elif [[ $# > 1 ]]
then
    echo -e "Boolean value: True (is greater than)"
    exit 1
else
    echo -e "Boolean value: False (is equal to)"
fi
```

| Operator | Description |
| -------- | ----------- |
| `!`      | logical NOT |
| `&&`     | logical AND |
| `\|\|`   | logical OR  |

```bash theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
if [[ -e "$1" && -r "$1" ]]
then
    echo -e "We can read the file that has been specified."
    exit 0
elif [[ ! -e "$1" ]]
then
    echo -e "The specified file does not exist."
    exit 2
elif [[ -e "$1" && ! -r "$1" ]]
then
    echo -e "We don't have read permission for this file."
    exit 1
else
    echo -e "Error occurred."
    exit 5
fi
```

## Debugging

Bash debugging is usually done with `-x` and `-v`.

* `bash -x script.sh` shows each command as it executes.
* `bash -x -v script.sh` also shows the code as Bash reads it.

```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ bash -x CIDR.sh

+ '[' 0 -eq 0 ']'
+ echo -e 'You need to specify the target domain.\n'
You need to specify the target domain.
```

```shellsession theme={"theme":{"light":"github-light","dark":"tokyo-night"}}
Dudji@htb[/htb]$ bash -x -v CIDR.sh

#!/bin/bash
if [ $# -eq 0 ]
then
    echo -e "You need to specify the target domain.\n"
    exit 1
fi
```
