The shell is a command-line interpreter that provides a user interface to an operating system's services. It allows users to interact with the system through text-based commands and scripts. Shell scripting refers to writing a series of commands in a script file to automate tasks and perform complex operations.
The shell makes heavy use of [Environment Variables](../../linux/Environment%20Variables.md) for storing settings and configuration.
## Usage
You can enter commands to be executed in the shell.
```shell
command
```
Commands can be either a script, a binary or anything that can be executed. If you don't provide a full path to the file you want to run the shell will search in the locations defined in the `$PATH` environment variable.
Everything after the command will be provided to the command as arguments. Each argument is separated by a space character. To avoid that you could quote an argument or escape the space character.
```shell
command argument1 argument2 argument3
# These two have identical arguments
command "argument 1" "argument 2" "argument 3"
command argument\ 1 argument\ 2 argument\ 3
```
Arguments are typically structured into flags and options.
Flags (like `-v` or `--verbose`) toggle specific functionality while options (like `-o output.txt` or `--out output.txt`) allow you to specify a value for the command.
### Running in the background
Append `&` to a command to run it in the background.
```shell
long-running-command &
```
### Running Commands Sequentially
The `;` symbol marks the end of a command. This allows you to execute multiple commands in one line.
```shell
command1 ; command2
```
### Globs
The shell supports globs like `*` for matching.
For example to delete all text files in the current directory:
In a shell environment, there are three standard streams:
- **Standard Input (stdin - fd 0):** Represents the input to a command.
- **Standard Output (stdout - fd 1):** Represents the output of a command.
- **Standard Error (stderr - fd 2):** Represents error messages generated by a command.
### **Output Redirection (`>` and `>>`)**
-`>` is used to redirect standard output to a file, overwriting the file's contents if it already exists.
```shell
echo "Hello, World!" > output.txt
```
-`>>` is used to redirect standard output to a file, appending to the file if it exists.
```shell
echo "More text" >> output.txt
```
### **Input Redirection (`<`)**
`<` is used to redirect standard input from a file.
```shell
while read line; do
echo "Line: $line"
done <input.txt
cat <input.txt
```
### **Error Redirection (`2>` and `2>>`)**
-`2>` is used to redirect standard error to a file, overwriting the file if it exists.
```shell
command_that_might_fail 2> error.log
```
-`2>>` is used to redirect standard error to a file, appending to the file if it exists.
```shell
command_that_might_fail 2>> error.log
```
### **Pipes (`|`)**
The pipe operator (`|`) allows the output of one command to be used as the input for another command. This enables the creation of powerful and concise command pipelines.
```shell
command1 | command2
```
Example: Counting Lines in a File
```shell
cat textfile.txt | wc -l
```
### **Combining Redirection and Pipes**
You can combine redirection and pipes to create more complex command sequences.
```shell
# Redirect stderr to a file, and then pipe the output to another command
command1 2> error.log | command2
```
### **Here Documents (`<<`)**
Here documents allow you to include multiple lines of input in a script or command. `EOF` stands for End Of File.
```shell
cat <<EOF
This is a
multi-line
text block.
EOF
```
### **Command Substitution (`$()`)**
Command substitution allows the output of a command to replace the command itself.
```shell
result=$(ls -l)
echo "Listing: $result"
```
### **Named Pipes (FIFOs)**
Named pipes, or FIFOs (First In, First Out), are special files used for inter-process communication.
```shell
mkfifo mypipe
command1 > mypipe & # Background process writing to the pipe
command2 <mypipe#Readingfromthepipe
```
### **tee Command**
The `tee` command reads from standard input and writes to standard output and files simultaneously.
`/dev/null` is a special file that discards all data written to it.
```shell
command > /dev/null # Redirects output to null
```
## Shell Scripting
Shell scripting involves writing a series of commands for the shell to execute, allowing automation and the creation of more complex programs. It's a powerful way to streamline tasks and manage system configurations.
### Shebang
The shebang is the first line of a script and it indicates which executable is used for the execution of the script. The syntax is `#!` followed by the absolute path to a executable.
For simple shell scripts add this to the file:
```shell
#!/bin/bash
```
Although if you are scripting in another language you could change it for example to `python`
```shell
#!/bin/python
```
### Comments
You can add comments to a script by using the `#` symbol.
```shell
# This is a comment
```
### Variables
You can store variables in a shell script by following a `key=value` syntax.
| == (Equality) | Compares two numbers, if both are same then returns true. | `[$a == $b ]` would return false. |
| != (Not Equality) | Compares two numbers, if both are different then returns true. | `[ $a != $b ]` would return true. |
##### Relational Operators
For example, following operators will work to check a relation between 10 and 20 as well as in between "10" and "20" but not in between "ten" and "twenty".
| **-eq** | Checks if the value of two operands are equal or not; if yes, then the condition becomes true. | `[ $a -eq $b ]` is not true. |
| **-ne** | Checks if the value of two operands are equal or not; if values are not equal, then the condition becomes true. | `[ $a -ne $b ]` is true. |
| **-gt** | Checks if the value of left operand is greater than the value of right operand; if yes, then the condition becomes true. | `[ $a -gt $b ]` is not true. |
| **-lt** | Checks if the value of left operand is less than the value of right operand; if yes, then the condition becomes true. | `[ $a -lt $b ]` is true. |
| **-ge** | Checks if the value of left operand is greater than or equal to the value of right operand; if yes, then the condition becomes true. | `[ $a -ge $b ]` is not true. |
| **-le** | Checks if the value of left operand is less than or equal to the value of right operand; if yes, then the condition becomes true. | `[ $a -le $b ]` is true. |
| **-o** | This is logical **OR**. If one of the operands is true, then the condition becomes true. | `[ $a -lt 20 -o $b -gt 100 ]` is true. |
| **-a** | This is logical **AND**. If both the operands are true, then the condition becomes true otherwise false. | `[ $a -lt 20 -a $b -gt 100 ]` is false. |
| **-b file** | Checks if file is a block special file; if yes, then the condition becomes true. | `[ -b $file ]` is false. |
| **-c file** | Checks if file is a character special file; if yes, then the condition becomes true. | `[ -c $file ]` is false. |
| **-d file** | Checks if file is a directory; if yes, then the condition becomes true. | `[ -d $file ]` is not true. |
| **-f file** | Checks if file is an ordinary file as opposed to a directory or special file; if yes, then the condition becomes true. | `[ -f $file ]` is true. |
| **-g file** | Checks if file has its set group ID (SGID) bit set; if yes, then the condition becomes true. | `[ -g $file ]` is false. |
| **-k file** | Checks if file has its sticky bit set; if yes, then the condition becomes true. | `[ -k $file ]` is false. |
| **-p file** | Checks if file is a named pipe; if yes, then the condition becomes true. | `[ -p $file ]` is false. |
| **-t file** | Checks if file descriptor is open and associated with a terminal; if yes, then the condition becomes true. | `[ -t $file ]` is false. |
| **-u file** | Checks if file has its Set User ID (SUID) bit set; if yes, then the condition becomes true. | `[ -u $file ]` is false. |
| **-r file** | Checks if file is readable; if yes, then the condition becomes true. | `[ -r $file ]` is true. |
| **-w file** | Checks if file is writable; if yes, then the condition becomes true. | `[ -w $file ]` is true. |
| **-x file** | Checks if file is executable; if yes, then the condition becomes true. | `[ -x $file ]` is true. |
| **-s file** | Checks if file has size greater than 0; if yes, then condition becomes true. | `[ -s $file ]` is true. |
| **-e file** | Checks if file exists; is true even if file is a directory but exists. | `[ -e $file ]` is true. |
### Loops
You can also use loops.
```shell
# For Loop
for fruit in "${fruits[@]}"; do
echo "Fruit: $fruit"
done
# Example: Loop over files
for file in *.txt; do
echo "File: $file";
done
# While Loop
count=0
while [ $count -lt 5 ]; do
echo "Count: $count"
((count++))
done
```
You can use a few commands to control the loop.
-`break` breaks out of the loop, ending it prematurely
-`continue` skips to the next iteration of the loop, but skipping everything coming after it
If you want to loop over every single line even if one line might contain spaces (from a command output for example) you can use this trick:
```shell
command | while IFS= read -r varName; do
echo "Working on $varName"
done
```
### Functions
You can define your own functions. The arguments you give to the functions can be accessed via `$1`, `$2`, `$n`, etc. The same way the arguments passed to a shell script can be accessed.