Bash (Bourne again Shell) is a shell for running commands on a Unix or Linux system.

Repeating Historical commands

$ !!           # last command again
$ !find        # last command starting with 'find' again
$ file `!!`    # show the file type for the file found in the previous command
$ ^file^cat    # redo previous command but replace 'file' with 'cat'

Get part of a variable (substring)

Pull out parts of a substring:

str="2023-10-12"

echo "${str:5:2}" # 10
echo "${str::4}" # 2023
echo "2022-${str:5}" # 2022-10-12

Can even get substrings from right side using a negative index:

str="backup.sql"

echo "original${str:(-4)}" # original.sql

String replacement

str="obin-linux_x64_bin"

echo "${str/x64/armhf}" # obin-linux_armhf_bin
echo "${str/bin/dist}" # odist-linux_x64_bin
echo "${str//bin/dist}" # odist-linux_x64_dist

Can also do it to file names and paths:

str="db_config_backup.zip"

echo "${str/%.zip/.conf}" # db_config_backup.conf
echo "${str/#db/settings}" # settings_config_backup.zip

Can also match with a wildcard:

str="db_config_backup.zip"

echo "${str/%.*/.bak}" # db_config_backup.conf
echo "${str/#*_/new}" # newbackup.zip

String regex

You can use grep and sed but faster is the inbuilt if match with =~ that is a bash built-in:

str="db_backup_2003.zip"

if [[ $str =~ 200[0-5]+ ]]; then
    echo "regex_matched"
fi

Once bash does a regex match, it typically stores all matches in the BASHREMATCH shell variable. This variable is a read-only array, and it stores the entire matched data in the first index. If you use sub-patterns, Bash incrementally keeps those matches in other indexes:

str="db_backup_2003.zip"

if [[ $str =~ (200[0-5])(.*)$ ]]; then
    echo "${BASH_REMATCH[0]}" # 2003.zip
    echo "${BASH_REMATCH[1]}" # 2003
    echo "${BASH_REMATCH[2]}" # .zip
fi

Similarly, can use a regex to do find and replace:

str="db_backup_2003.zip"
re="200[0-3].zip"

echo "${str/$re/new}.bak" # db_backup_new.bak