Skip to main content

ELL Blog

Bash & Linux Helpers

It seems that many article tutorials on bash are ugly, are suboptimal, and are no straight forward. My goal is for this file to be a reference file whenever I need to do scripting after a long time. One important concept in bash, is that an exit code of 0 indicates True, not False. Which seems counterintuitive if you program in any non-scripting language like C/C++ or Python.

Bash - Argument Parsing and Flags

If you don’t get what’s going on, read my bash quickstart article

Concepts

  • getopts is overkill, so here is an alternative.
  • “Booleans” using string comparison
  • Conditionals (if, elif, else, AND &&, OR ||)
  • For loop
supplied_arg="default"
# iterate all arguments
for arg in "$@"; do
    # use [[ ]] to avoid double quoting variables
    if [[ $last_arg = "--supply" ]]; then
        supplied_arg="$arg"
        last_arg=""
    # OR ||
    elif [[ $arg = "--flag1" ]] || [[ $arg = "--flag" ]]; then
        flag1=true
    # elif, AND &&
    elif [[ $arg = "--override" ]] && [[ ! $supplied_arg = "default" ]]; then
        # ignore the redundancy, this is to showcase &&
        flag_override=true
    else
        # you can do something with these args here
        # wc -l "$arg"
        # or you can set last_arg in order to parse `--something ARG`
        last_arg="$arg"
    # end of if statement
    fi
# done loop (for or while)
done

# check if flag supplied
if [ flag1 = true]; then
    echo "got --flag1 or --flag"
else
    notGot="got neither --flag1 nor --flag"
fi
# flag not supplied (since variable was never set, we check ! = true rather than = false)
if [ ! flag1 = true ]; then
    echo $notGot
fi

Linux - Add to PATH

  • A better approach than appending a statement to PATH="$PATH:/new/path">> ~/.bashrc
  • TODO: add instructions

Linux - Add Cronjob

  • automated approach
  • adds the cronjob if not exists
  • prevents concurrent/overlap runs
add_cronjob() {
    # crontab will not set the working directory
    # create locks directory if it does not exist
    mkdir -p ~/locks
    # my sample job uses flock to prevent overlapping runs
    # minute hour day month day_of_week
    # /X means divisible by X
    cronjob="*/1 * * * * flock -n ~/locks/auto_deploy_$PROJECT.lock $PYTHON $(pwd)/cronjob.py"
    # cronjob.py will set the working directory
    if ! crontab -l &>/dev/null; then
        # crontab file does not exist
        echo "$cronjob" | crontab -
        echo "Created new crontab with job"
    elif ! crontab -l 2>/dev/null | grep -Fq "$cronjob"; then
        # job not found in crontab
        echo $(crontab -l ; echo "$cronjob") | crontab -
        echo "Added $cronjob ... to existing crontab"
    else
        echo "Job already exists in crontab"
    fi
}

Linux - Add Systemd Service

  • I used lots of echos and tee [-a]
  • In order to work with spaces in an argument, I created strings with \'$VAR\'
  • TODO: add code

Linux - Auto Setup Certbot

# Context:
# define $DOMAIN (e.g. lenerva.com)
install_certbot() {
    sudo snap install core; sudo snap refresh core
    sudo snap install --classic certbot
    sudo ln -s /snap/bin/certbot /usr/bin/certbot
    sudo certbot --nginx -d $DOMAIN -d www.$DOMAIN
}

Linux - Auto Configure NGINX - Gunicorn

This works for me but it is possible to fail for you without editing /etc/nginx/nginx.conf manually server name hashes to 128

# Context:
# define $DOMAIN beforehand (e.g. lenerva.com)
# define $PROJECT beforehand (e.g. my-project)
# pwd is currently the project root directory
configure_nginx() {
    # configure nginx
    sudo apt install nginx -y

    sudo mkdir -p /var/www/$DOMAIN/html
    sudo chown -R "$USER":"$USER" /var/www/$DOMAIN/html
    sudo chmod -R 755 /var/www/$DOMAIN

    echo "server {" | sudo tee /etc/nginx/sites-available/$PROJECT >/dev/null
    echo "  listen [::]:80;" | sudo tee -a /etc/nginx/sites-available/$PROJECT >/dev/null
    echo "  listen 80;" | sudo tee -a /etc/nginx/sites-available/$PROJECT >/dev/null
    echo "  location / {" | sudo tee -a /etc/nginx/sites-available/$PROJECT >/dev/null
    echo "    include proxy_params;" | sudo tee -a /etc/nginx/sites-available/$PROJECT >/dev/null
    echo "    proxy_pass http://unix:$(pwd)/gunicorn.sock;" | sudo tee -a /etc/nginx/sites-available/$PROJECT >/dev/null
    echo "  }" | sudo tee -a /etc/nginx/sites-available/$PROJECT >/dev/null
    echo "}" | sudo tee -a /etc/nginx/sites-available/$PROJECT >/dev/null

    sudo ln -s "/etc/nginx/sites-available/$PROJECT" /etc/nginx/sites-enabled

    # TODO: if publishing script, also set max server name hashes to 128 in /etc/nginx/nginx.conf
    # uncomment below if it didn't work
    # nano /etc/nginx/nginx.conf
    sudo systemctl restart nginx
    # Firewall
    sudo ufw allow 'Nginx Full'
}