# Conditional Expressions

Conditionals are how your scripts make decisions: run different code based on a true/false outcome.

In Bash, `[[` is your go-to for testing files, strings, and numbers.

Let's start with a basic example:

```bash
touch conditional_test.sh
```

Open `conditional_test.sh` and add:

```bash
#!/usr/bin/env bash

my_var="data"

if [[ -n "$my_var" ]]; then # If variable is not empty
  echo "Variable contains data."
fi
```

Save, make executable, and run:

```bash
chmod +x conditional_test.sh
./conditional_test.sh
```

Output: `Variable contains data.`

Now, for your full reference:

## File Tests

These check file attributes. Use `"${file}"` to handle spaces in names.

* `[[ -e "${file}" ]]` - True if `file` exists.
* `[[ -f "${file}" ]]` - True if `file` exists and is a regular file.
* `[[ -d "${file}" ]]` - True if `file` exists and is a directory.
* `[[ -s "${file}" ]]` - True if `file` exists and is not empty (size > 0).
* `[[ -r "${file}" ]]` - True if `file` exists and is readable.
* `[[ -w "${file}" ]]` - True if `file` exists and is writable.
* `[[ -x "${file}" ]]` - True if `file` exists and is executable.
* `[[ -L "${file}" ]]` - True if `file` is a symbolic link (`-h` is also used).
* `[[ -b "${file}" ]]` - True if `file` exists and is a block device.
* `[[ -c "${file}" ]]` - True if `file` exists and is a character device.

## String Tests

Compare string properties and values.

* `[[ -v "${varname}" ]]` - True if `varname` is set (has a value).
* `[[ -z "${string}" ]]` - True if `string` has zero length (empty).
* `[[ -n "${string}" ]]` - True if `string` has non-zero length (not empty).
* `[[ "${string1}" == "${string2}" ]]` - True if strings are equal. (`==` allows pattern matching inside `[[`.)
* `[[ "${string1}" != "${string2}" ]]` - True if strings are not equal.
* `[[ "${string1}" < "${string2}" ]]` - True if `string1` sorts before `string2` lexicographically.
* `[[ "${string1}" > "${string2}" ]]` - True if `string1` sorts after `string2` lexicographically.

## Numeric Comparisons

Compare integer values.

* `[[ ${arg1} -eq ${arg2} ]]` - True if `arg1` equals `arg2`.
* `[[ ${arg1} -ne ${arg2} ]]` - True if `arg1` not equals `arg2`.
* `[[ ${arg1} -lt ${arg2} ]]` - True if `arg1` less than `arg2`.
* `[[ ${arg1} -le ${arg2} ]]` - True if `arg1` less than or equal to `arg2`.
* `[[ ${arg1} -gt ${arg2} ]]` - True if `arg1` greater than `arg2`.
* `[[ ${arg1} -ge ${arg2} ]]` - True if `arg1` greater than or equal to `arg2`.

## Logical Operators

Combine multiple conditions.

* **AND (`&&`)**: Both conditions must be true.
    ```bash
    # Example: file exists AND is not empty
    if [[ -f "my_file.txt" && -s "my_file.txt" ]]; then
        echo "File is present and has content."
    fi
    ```
* **OR (`||`)**: At least one condition must be true.
    ```bash
    # Example: either file or directory exists
    if [[ -f "another_file.txt" || -d "another_dir" ]]; then
        echo "Found a file or a directory."
    fi
    ```

## Exit Status Checks

`$?:` The exit status of the *last* executed command. `0` means success. Any non-zero value indicates an error.

* **Success:**
    ```bash
    command_to_run
    if [[ $? -eq 0 ]]; then
      echo "Command succeeded."
    fi
    ```
* **Failure:**
    ```bash
    command_to_run
    if [[ $? -ne 0 ]]; then
      echo "Command failed."
    fi
    ```
