The magic of bash

Personal notes on some bash magic

So, let’s learn some bash

Bash is an old language but by learning it we can leverage it’s amazing benefits. As a developer you can use bash to automate tasks in a server environment, such a CI/CD, without having to create a Node or Python script. It is always awesome to have one more tool in your developer’s toolbelt 😉.

This are my personal study notes on Cameron Nokes course about bash in the egghead platform. If you can, check it out here!. It’s amazing

Commands

I won’t write the ordinary commands such as ls and cd.

/home/myuser


  drwxr-xr-x    5 user group 4096 out 18 03:42 public
  -rw-r--r--    1 user group 5453 mar 31  2020 README.md
  drwxr-xr-x    8 user group 4096 mar 31  2020 src
  drwxr-xr-x    2 user group 4096 set 22 13:36 static

The first column indicates weather it’s a file or folder and it’s access rules. If starts with a - is a file, otherwise d stands for directory.

Then we have the number of hard links (see this article), user and the groups that file/folder belongs, the size, date it was created or last modified, and the name.

A better way to view big files is:

Executing scripts

After creating a bash file such as script.sh, you need to give it execute permission with chmod u+x script.sh

Variables are referenced in bash with $. You can get command line arguments with it’s number passed after the executing script. So to get the first argument, just reference the $1.


  echo "$1 world"

  # executing
  ./script.sh Hello

To execute a bash command in a line, do $(command):


  echo "Initializing project in directory $(pwd)"
  git init
  npm init -y
  mkdir project
  touch project/index.js
  # anything else you might want to do

If we create an awesome script and want to execute it through the whole computer, we can add it to our $PATH. The $PATH is just a comma separated list of directories your computer looks for executables. You can inspect with echo $PATH


  /home/user/.nvm/versions/node/v10.16.1/bin:/home/user/bin:/usr/local/bin:/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin:/home/user/go/bin

If we run which node to see where our nodejs executable is:


  /home/user/.nvm/versions/node/v10.16.1/bin/node

It give us the same path directory of the outputed $PATH.

To add some file to your $PATH, we can just cp my-script.sh /usr/local/bin/my-script because the path /usr/local/bin already is in our $PATH so everything inside will be executable too.

Variables

To create a variable in bash we do variableName=123. And to reference it we use $variableName.

The variable is only available for that shell session and only in that scope. To make a variable public for any scope in that shell session, we do export variableName.

We can also unset a variable with unset variableName

There are lots of global variables. We can see it ith env command.

A script to create a temporary directory and clone a git repository in it:


  temp=$(mktemp -d) # create a temp directory and store the path inside the temp variable
  git clone --branch $1 $PWD $temp # clone some branch from the current repository into the temp folder
  echo "branch $1 cloned to $temp"
  # run other tasks or tests

To run this script we do ./script.sh new-branch

Functions

It can be seen as small scripts. To pass arguments, it works the same as passing args for scripts in command line. You reference them by their order number. We don’t name the arguments inside the ()


  greet() {
    echo "$1 World"
  }

  greet "Hey"

We can store the function result in a variable like:


  greet() {
    echo "$1 world!"
  }

  greeting=$(greet "Hello")

  echo "The greeting is $greeting"

An function can access global variables and local variables too. To declare local scoped variables, use the local keyword.


  test() {
    local local_var="I am local!"
  }

Exit status

To get the exit status of a bash command, use $?. The response is a number from 0 to 255


  ls nonexist
  echo $?
  # 1

above error of 1 is a general error. The $? always get the error for the previous command.

Conditionals


  if [[ $USER = 'myuser' ]]; then
    echo "true"
  elif [[ 1 -eq 1 ]]; then
    echo "yayy"
  else
    echo "false"
  fi

There are different comparisson syntax:

We can also use ifs like a ternary style:


  [[ $USER = 'myuser' ]] && echo "yess" || echo "false"

To make a request and get just the status code:


  curl -ILs https://example.org | head -n 1 | cut -d ' ' -f 2

  # make a request with -I to get just the header, L to make redirects and s to be silent and don't show any progress
  # then we pipe the result with the head command to get the header and get just the first line, which is the status code
  # it will return HTTP/2 200
  # then we can cut the response to get just the number 200
  # we used the cut command, -d of delimiter flag to cut between the spaces and -f to get the second part of the cut

Pipes

To show all running processes of the computer: ps ax. It will log a bunch of lines of processes. But if we want to filter just the chrome instances, we could pipe and grep.


  ps ax | grep Chrome

With pipe, the output of the previous command will be the input of the next one