Table of contents
Display info using echo command
echo Hello World!
echo command ignores\n . In order not to skip\n use-e parameter.echo command can be used to return value from the function. In this case, it should be used as a last statement in the function or command. We'll take a look at that further.
Directory location
Print working directory
pwd
cd command
Change directory
cd a/b/c
Change directory to the previous
cd -
Navigate user home directory
cd ~
Quickly switch between the current and previous directory with pushd and popd
pushd a/b/c # now in a/b/c
cd ../ # now in a/b
cd /usr/share # now in /usr/share
popd # now in the initial location
Variables
Variables usage
name="Oleh"
echo "My name is $name"
echo "or, My name is ${name}"
- When you assign value to a variable, you must not include the space around the equal sign, like the following
var=123 . - As a convention, you should use lowercase underscore variable names for regular variables.
For environment variable names, use uppercase with underscore.
${variable_name} vs $variable_name
greet_ending_1='i'
greet_ending_2='ello'
echo "h${greet_ending_1} John, or better to say h${greet_ending_2}"
Otherwise, regular
Environment
Adding environment variables
To add an environment variable, we can use
export MY_VAR="12345"
Printing environment variables
export -p
envsubst command
.bashrc:
export GREETINGS='Hello'
export FIRST_NAME='Oleh'
export SECOND_NAME='Baranovskyi'
hello.txt
$GREETINGS, $FIRST_NAME $SECOND_NAME. How are you?
if we run:
envsubst < hello.txt
result:
Hello, Oleh Baranovskyi. How are you?
It's pretty handy, as we can write our configs:
db-template.json:
{
"type": "$DB_TYPE",
"host": "DB_HOST",
"port": "DB_PORT",
"username": "DB_USERNAME",
"database": "$DB_NAME",
"password": "$DB_PASS"
}
some remote .bashrc:
export DB_TYPE='mysql'
export DB_HOST='localhost'
export DB_PORT='3306'
export DB_USERNAME='obaranovskyi'
export DB_PASS='my_password'
export DB_NAME='my_db'
run:
envsubst < db-template.json > db.json
db.json:
{
"type": "mysql",
"host": "localhost",
"port": "3306",
"username": "obaranovskyi",
"database": "my_db",
"password": "my_password"
}
Arguments
Read user input
read -p "Enter your name: " username
echo "Hello $username, nice to meet you!"
Reading the arguments
$1 ,$2 ,$3 ... - get argument by number@ - list of passed arguments$# - variable returns the input size
Loop through the arguments
i=1;
for arg in "$@"
do
echo "Arg - $i: $arg";
i=$((i + 1));
done
Shift the position of the command line arguments
i=1;
j=$#;
while [ $i -le $j ]
do
echo "Username - $i: $1";
i=$((i + 1));
shift 1;
done
Parse options with getopts
while getopts u:a:f: flag
do
case "${flag}" in
u) username=${OPTARG};;
a) age=${OPTARG};;
f) fullname=${OPTARG};;
esac
done
echo "username: $username"
echo "age: $age"
echo "fullname: $fullname"
To run, enter in terminal:
./your-script-name.sh -u oleh -a 18 -f baranovskyi
Parameter expansion
Positional Parameters
# script name
echo "$0"
# first argument
echo "$1"
Special parameters
# number of arguments
echo "$#"
# last exited status
ls non-existent-file
echo "$?"
# shell process ID
echo "$$"
Variable assignment & expansion
# `${variable}` Retrieve the value of the variable.
my_var="Hello, Bash"
echo "Value is: ${my_var}"
# ${variable:-default}: Use the default value if the variable is unset or null.
echo "${unset_var1:-defaultValue}"
echo "${unset_var1}" # ''
# ${variable:=default}: Set the variable to the default value if unset or null.
echo "${unset_var:=defaultValue}"
echo "$unset_var1" # defaultValue
# ${variable:+alternate}: Use the alternate value if the variable is set and not null.
unset_var2=""
echo "---${unset_var2:+alternateValue}" # ''
unset_var2="Hello"
echo "${unset_var2:+alternateValue}" # alternateValue
# ${#variable}: Length of the variable value.
my_var="obaranovskyi"
echo "${#my_var}" # 12
String manipulation
# ${variable#pattern}: Remove the shortest match of pattern from the beginning.
path="/path/to/file.txt"
echo "${path#*/}" # path/to/file.txt
# ${variable##pattern}: Remove the longest match of pattern from the beginning.
path="/path/to/file.txt"
echo "${path##*/}" # file.txt
# ${variable%pattern}: Remove the shortest match of pattern from the end.
filename="file.d.ts"
echo "${filename%.*}" # file.d
# ${variable%%pattern}: Remove the longest match of pattern from the end.
filename="file.d.ts"
echo "${filename%%.*}" # file
# ${variable/pattern/replacement}: Replace the first match of pattern with replacement.
sentence="Bash is fun."
echo "${sentence/is/IS}" # Bash IS fun.
# ${variable//pattern/replacement}: Replace all matches of pattern with replacement.
sentence="Bash is fun. Bash is powerful."
echo "${sentence//Bash/Shell}" # Shell is fun. Shell is powerful.
Substring extraction
# `${variable:offset:length}: Extract a substring starting from the offset with a specified length.`
string="Hello, Bash!"
echo "${string:7:4}" # Bash
Default values
# ${variable:-default}: Use the default value if the variable is unset or null.
echo "${unset_var:-defaultValue}" # defaultValue
# ${variable:=default}: Set the variable to the default value if unset or null.
echo "${unset_var:=defaultValue}" # defaultValue
Command substitution
# $(command): Execute command and substitute its output.
current_date=$(date)
echo "${current_date}" # Sun Nov 26 08:15:40 EET 2023
Arithmetic operations
# $((expression)): Perform arithmetic operations.
result=$((5 + 3))
echo "${result}" # 8
Conditional Substitution
# ${variable+value}: Use value if variable is set.
# Default path
default_path="/usr/local/bin"
# Use the provided path if available, otherwise use the default
custom_path="${1:+$1}"
final_path="${custom_path:-$default_path}"
echo "Selected path: $final_path"
Indirect Reference
# ${!variable}: Reference the variable indirectly.
first_var="Hello, Bash!"
reference="first_var"
echo "${!reference}" # Hello, Bash!
Array Variables
# ${array[index]}: Access elements in an array.
my_array=("Apple" "Banana" "Orange")
echo "${my_array[1]}" # Banana
# ${#array[@]}: Number of elements in the array.
echo "${#my_array[@]}" # 3
Conditionals
test command
It just takes an expression and calculates.
The expression result is stored in
The possible results mean the following:
0 - is successful (akatrue )1 and higher - is failure (akafalse )
var=5
test $var -eq 2 && echo true || echo false
echo $?
The output:
false
0
The previous code also can be written in the following way:
var=5
[ $var -eq 2 ] && echo true || echo false
echo $?
if statement
- When you use
if statements, the space has to be around the square brackets ([ , and] ),if [ ... ]
if [[ "$username" == "Oleh" ]];
then
echo "Your name is Oleh"
fi
if -else
if [[ "$username" == "Oleh" ]];
then
echo "Your username is Oleh"
else
echo "Your username is NOT Oleh"
fi
else-if (elif )
if [[ "$username" == "Oleh" ]];
then
echo "Your username is Oleh"
elif [[ "$username" == "Lisa" ]];
then
echo "Your username is Lisa"
else
echo "Your username is NOT Oleh or Lisa"
fi
case statement
read -p "Are you 21 or over? Y/N " user_answer
case "$user_answer" in
[yY] | [yY][eE][sS])
echo "You can have a beer :)"
;;
[nN] | [nN][oO])
echo "Sorry, no drinking"
;;
*)
echo "Please enter y/yes or n/no"
;;
esac
Or
value="orange"
# Single-bracket syntax
# if [ "$value" == "apple" -o "$value" == "orange" ];
# Double-bracket syntax
if [[ "$value" == "apple" || "$value" == "orange" ]];
then
echo "It's a fruit."
else
echo "It's not a fruit."
fi
And
value="orange"
# Single-bracket syntax
# if [ "$value" == "apple" -a "$value" == "orange" ];
# Double-bracket syntax
if [[ -n "$value" && "$value" == "orange" ]];
then
echo "It's a fruit."
else
echo "It's not a fruit."
fi
Comparison
num1=31
num2=5
if [ "$num1" -gt "$num2" ];
then
echo "$num1 is greater than $num2"
else
echo "$num1 is less than $num2"
fi
Another syntax for arithmetic comparison is
value=7
if (( $value <= 7 ));
then
echo "Greater than or equal to 7"
else
echo "Less than 7"
fi
$value1 -eq $value2 - equal$value1 -ne $value2 - not equal$value1 -ge $value2 - greater than or equal$value1 -gt $value2 - greater than$value1 -le $value2 - less than or equal$value1 -lt $value2 - less than
Number
Calculations
value1=10
value2=20
# Option 1: Using expr command
value3=$(expr $value1 + $value2)
echo $value3 # 30
# Option 2: Using double parenthesis
value3=$((value1 + value2))
echo $value3 # 30
# Option 3: Using <md-code>let</md-code> command
let value3=value1+value2
echo $value3 # 30
# Option 4: Using
declare -i value3=value1+value2
echo $value3 # 30
# Option 5: Using Bash arithmetic expansion
value3=$((value1 + value2)) # 30
echo $value3
Calculating with bc command
To calculate decimal numbers in Bash, we can use the
echo "$value1 + $value2 * 7.33" | bc # 156.60
# To assign value to the variable
value3=$(bc <<< "$value1 + $value2 * 7.33")
echo $value3 # 156.60
String
String comparison
$value1 = $value2 and$value1 == $value2 - equal.- Use
== with the[[ , and= with thetest [
- Use
$value1 != $value2 - not equal$value1 =~ regex - match$value with a regex,$value1 == "Test" check if$value1 contains word"Test"
-n $value - check if the$value length is non-zero-z $value - check if the$value length is zero
$value1="Value1"
$value2="Value2"
# Option 1: with the single square brackets
# if [ "$value1" = "$value2" ]; then
# Option 2: with the double square brackets
if [[ "$value1" == "$value2" ]]; then
echo "Equal"
else
echo "Not equal."
fi
Good advice will be to use space around the square brackets and to keep your variable in double quotes so that you won't have any issues with the multiple words.
Check if string is empty
value1=''
if [[ -z $value1 ]]; then
echo "Empty"
fi
value2='Not empty value'
if [[ -n $value2 ]]; then
echo "Not empty"
fi
Check string length
value="Some value"
echo "Length is: ${#value}" # Length is: 10
Range
touch {1..10}.txt
touch {a..z}.txt
touch {A..Z}.txt
for i in {000..100}
do
echo Hello > "File${i}.txt"
done
File conditions
FILE="file.txt"
if [ -e "$FILE" ];
then
echo "$FILE exists"
else
echo "$FILE does not exist"
fi
-d file - is the file a directory-f file - is the file-g file - is the group id set on a file-r file - is the file readable-s file - is the file with a non-zero size-u - is the user id set on a file-w - is the file writable-x - is the file executable
Arrays
Array basics
# Define array
animals=('Dog' 'Horse' 'Cow')
# Print values by index
echo "Animal with index [0] is: ${animals[0]}" # Dog
echo "Animal with index [1] is: ${animals[1]}" # Horse
echo "Animal with index [2] is: ${animals[2]}" # Cow
array_length=${#animals[@]}
echo "Array length: ${array_length}"
last_index=$(($array_length-1))
echo "Last element: ${animals[$last_index]}"
echo "Space-separated elements: ${animals[@]}"
echo "Space-separated keys: ${!animals[@]}"
# Add new elements
animals[3]='Rabbit'
animals[4]='Frog'
animals[5]='Bird'
echo "Animal with index [3] is: ${animals[0]}" # Rabbit
echo "Animal with index [4] is: ${animals[1]}" # Frog
echo "Animal with index [5] is: ${animals[2]}" # Bird
# Update value by index
animals[1]="Cat"
echo "${animals[1]}" # is Cat not Horse anymore
echo "Range from 3, length 2 ${animals[@]:3:2}" # Rabbit Frog
Iterate through array
animals=('Dog' 'Horse' 'Cow')
for i in "${animals[@]}"; do
echo "$i"
done
Array operations
animals=('Dog' 'Horse' 'Cow')
# Push new elements
animals+=('Bird')
animals=("${animals[@]}" "Frog")
echo "${animals[@]}" # Dog Horse Cow Bird Frog
# Filter with regex
animals=( "${animals[@]/Fr*/}" )
echo "${animals[@]}" # Dog Horse Cow Bird
# Remove element by index
unset animals[0]
echo "${animals[@]}" # Horse Cow Bird
# Duplicate
animals2=("${animals[@]}")
echo "${animals2[@]}" # Horse Cow Bird
# Read array from the file
# Content of animals.txt:
# Fish
# Octopus
# Shark
sea_animals=(`cat "animals.txt"`)
echo "${sea_animals[@]}"
# Concatenation
concatenated_animals=("${animals[@]}${sea_animals[@]}")
echo "${concatenated_animals[@]}" # Horse Cow Bird Fish Octopus Shark
Dictionary
Dictionary Basics
# Define dictionary
declare -A user
# Set properties
user[first_name]="Oleh"
user[second_name]="Baranovskyi"
echo "${user[first_name]}" # Oleh
# Print all values
echo "${user[@]}" # Oleh Baranovskyi
# Print all keys
echo "${!user[@]}" # first_name second_name
# Print number of elements
echo "${#user[@]}" # 2
# Remove by key
unset user[second_name]
Iterate over keys and values
declare -A user
user[first_name]="Oleh"
user[second_name]="Baranovskyi"
# Iterate over values
for value in "${user[@]}"; do
echo "$value"
done
# Iterate over keys
for key in "${!user[@]}"; do
echo "$key"
done
Loops
Basic for loop
for i in 1 2 3 4 5
do
echo "Value is: $i"
done
C-like loop
for ((i = 0 ; i < 100 ; i++)); do
echo "Index is: $i"
done
Ranges
for i in {1..5}; do
echo "Value is: $i"
done
Reading Lines
line=1
while read -r current_line
do
echo "$line: $current_line"
((line++))
done < "./file.txt"
Forever
while true; do
···
done
Finding matches with a grep command
grep flags
Flag | Description |
---|---|
|
Ignore case distinctions in both the pattern and input files. |
|
Invert the sense of matching, selecting non-matching lines. |
|
Suppress normal output; display count of matching lines for each input file. |
|
Prefix each line of output with the 1-based line number within its input file. |
|
Suppress normal output; instead print the name of each input file with at least one match. |
|
Read all files under each directory, recursively. |
|
Select only those lines containing matches that form whole words. |
|
Print |
|
Print |
|
Print |
|
Interpret the pattern as an extended regular expression (ERE). |
|
Interpret the pattern as a list of fixed strings, separated by newlines. |
|
Show only the part of a matching line that matches the pattern. |
|
Mark up the matching text with the specified color (auto, always, never). |
|
Skip files and directories matching the specified pattern. |
|
Search only files that match the specified pattern. |
Search through file
grep 'some text' file.txt
Search in commands history
history | grep 'cd'
to make it more interactive,
history | grep 'cd' | less
Search for process
ps aux | grep nginx
If you're often searching for processes you might consider
Using flags
# Search for 'in' word but not as a part of
# another word and use for search ignore case
grep -wi "in"
# Search for word 'in', with ignore case,
# line numbers, and recursively
grep -winr "in"
# Search for word 'in', with ignore case, recursively,
# and show file name instead of occurrence
grep -wirl "in"
# Search for occurrence using the regex
grep -P "\d{3}-\d{3}-\d{4}" <filename>.txt
Aliases
Create an alias
alias ll='ls -al'
Remove an alias
unalias ll
Creating aliases dynamically
This script will create aliases for all subdirectories in the my-configs/python
directory.
Each of these directories holds a main.py
file, and the alias will run this file.
Notice that we use eval
to create the alias dynamically.
current_dir="${HOME}/my-configs/python"
# Loop over all subdirectories
for dir in $(find $current_dir -maxdepth 1 -type d)
do
dir_name=$(basename $dir)
# Skip the current directory
if [ "$dir_name" = "." ] || [ "$dir_name" = "python" ]
then
continue
fi
command_to_alias="py ${dir}/main.py"
alias_name=$dir_name
eval alias $alias_name="'$command_to_alias'"
done
User
Add user
sudo useradd obaranovskyi
Set user password
sudo passwd username
Remove user
userdel obaranovskyi --force
Permissions
Check file permissions and ownership
ls -l text.txt
drw-rw-r-- 1 obaranovskyi staff 18 2020-07-17 17:18 text.txt
drw-rw-r-- - file permissionsobaranovskyi - ownerstaff - group
File permissions
Permission | Description |
---|---|
User | File owner |
Group | A group or user can own the file. Everyone in the group will have the same permissions. |
Other | Everyone else, or permissions for the world. |
Characters representing the permissions
Character | Description |
---|---|
|
Read permissions |
|
Write permissions |
|
Execute permissions |
|
No permissions |
Absolute/Numeric mode
Number | Permission Type | Symbol |
---|---|---|
0 | No Permissions |
|
1 | Execute |
|
2 | Write |
|
3 | Execute + Write |
|
4 | Read |
|
5 | Read + Execute |
|
6 | Read + Write |
|
7 | Read + Write + Execute |
|
Using chmod
Symbolic mode
Symbol | Description |
---|---|
|
Adds a permission to a file/directory |
|
Removes the permission |
|
Sets the permissions |
User denotations
User denotation | Description |
---|---|
|
user/owner |
|
group |
|
other |
|
all |
a few example:
chmod o=rwx file.txt # set permissions to the others
chmod g+x file.txt # add execute permissions to the group
chmod u-r file.txt # remove read permission for the user
Using chown to change ownership
chown user filename # Changing the ownership of a file/directory
chown user:group filename # Changing user and group ownership
a few examples:
sudo chown obaranovskyi text.txt # Changing the file ownership to `obaranovskyi`
Using chgrp to change group
sudo chgrp obaranovskyi text.txt
Note: to print existing groups type
Streams
Stream types
Stream | Description |
---|---|
|
standard input stream (STDIN) |
|
standard output stream (STDOUT) |
|
standard error stream (STDERR) |
Stream redirections
We can redirect streams in the following way:
Stream | Description |
---|---|
|
STDOUT to STDERR |
|
STDERR to STDOUT |
|
STDOUT to nowhere |
|
STDERR to nowhere |
|
Pass input from file or I/O device data to a command (wc -l < text.txt) |
Reading streams on different levels
We have this source:
function calc_value() {
echo "[Debug] Value calculation:1"
echo "[Debug] Value calculation:2"
echo "[Debug] Value calculation:3"
# The result of multiple calculations :)
echo 12345
}
echo "The result is: $(calc_value)"
# Output:
# The result is: [Debug] Value calculation:1
# [Debug] Value calculation:2
# [Debug] Value calculation:3
# 12345
Obviously, this is not what we want. In some cases, we want to see the debug log and, in some cases, the result.
Let's write this function in this way:
function calc_value() {
echo "[Debug] Value calculation:1" 1>&2
echo "[Debug] Value calculation:2" 1>&2
echo "[Debug] Value calculation:3" 1>&2
# The result of multiple calculations :)
echo 12345
}
echo "The result is: $(calc_value)"
# Output:
# [Debug] Value calculation:1
# [Debug] Value calculation:2
# [Debug] Value calculation:3
# The result is: 12345
Looks better now, as everything is in place.
To see only the result line:
echo "The result is $(calc_value 2>/dev/null)"
# Output:
# The result is: 12345
To see only the debug logs:
calc_value 2>&1 1>/dev/null
# Output:
# [Debug] Value calculation:1
# [Debug] Value calculation:2
# [Debug] Value calculation:3