Introduction to the CLI

Bash Scripting 101

Santiago Álvarez Rodríguez
Front-end Dev at PSL
santiaro90@gmail.com

You've done a long and productive way so far, and have got now quite a tool belt to tackle your day-to-day shell tasks.

But, you're probably tired of issuing the same set of commands time and time again, aren't you?

Well, that's what scripting's for. Write once, use... You know what's next ;)

Let's get cracking!

Environment Variables

You've most likely used at least one of the following variables in a command, haven't you?

$HOME
$SHELL
$PWD
$PATH
$EDITOR
$PAGER

Those are environment (or global) variables, and are available for every shell you open, and that includes any script you run (which happens to open a new shell).

You can see what your ENV looks like with the command—surprise, surprise— env

~/Documents $ env
TERM_SESSION_ID=w0t0p1:437759BD-70D8-49A3-8C83-223C9C696F54
SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.Hh6ngdz2Oz/Listeners
Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.hGGVzIPLou/Render
COLORFGBG=7;0
ITERM_PROFILE=Railscasts
XPC_FLAGS=0x0
PWD=/Users/santiaro90
SHELL=/usr/local/bin/zsh
SECURITYSESSIONID=186a5
TERM_PROGRAM_VERSION=3.0.9
TERM_PROGRAM=iTerm.app
PATH=/Users/santiaro90/.rbenv/shims:/Users/santiaro90/.nvm/versions/node/v5.2.0/bin:/usr/local/bin:/usr/local/sbin:/Users/santiaro90/.rbenv/bin:/Users/santiaro90/.gitcmd:/usr/bin:/bin:/usr/sbin:/sbin:/Users/santiaro90/go/bin:/usr/local/opt/go/libexec/bin
COMMAND_MODE=unix2003
TERM=xterm-256color
HOME=/Users/santiaro90
TMPDIR=/var/folders/my/r94pyxmn66z20271_9fwshh80000gn/T/
USER=santiaro90
XPC_SERVICE_NAME=0
LOGNAME=santiaro90
ITERM_SESSION_ID=w0t0p1:437759BD-70D8-49A3-8C83-223C9C696F54
__CF_USER_TEXT_ENCODING=0x1F5:0x0:0x0
SHLVL=1
OLDPWD=/Users/santiaro90
DOTDIR=/Users/santiaro90/.dotfiles
EDITOR=vim
VISUAL=vim
# more variables below...

See how all of them are uppercase?

That's the first best practice you'll learn for Bash scripting: always use lowercase for your script's variables, so you don't risk conflicting with your ENV.

Now let's see what some of the variables are for...

PATH Tells Bash where all your executables reside. Any script within a directory included in the PATH can be run by just its name (like ls, cut, find, vim, etc.). Directories in PATH are separated from each other by a colon.
HOME The home directory for the current user.
SHELL The shell you're using.
PWD The current directory.
HISTFILE Where the shell history gets saved.
CDPATH This is where cd will look for directories you try to access when not giving absolute paths.

My First Script

Let's create a file and open it in our favourite text editor

~/Documents $ touch my_first_script.sh

# Kinda biased here!
~/Documents $ vim my_first_script.sh

I guess you know what's coming... Yup, we're writing a "Hello, world"

#!/usr/bin/env bash
echo 'Hello, world'
# First, give execution permissions to your file
~/Documents $ chmod u+x my_first_script.sh

~/Document $ ./my_first_script.sh
Hello, world

See it in action!

A couple of things to notice here:

#!/usr/bin/env bash
echo 'Hello, world'
# First, give execution permissions to your file
~/Documents $ chmod u+x my_first_script.sh

~/Document $ ./my_first_script.sh
Hello, world

The #! is known as the shebang, and it's followed by the interpreter that will execute the commands in this file.

Because we told which interpreter to use right in the file, we can run the script like this.

Adding Variables

#!/usr/bin/env bash

# Without the !, this line is just a comment

# Remember what I told you about lowercase/uppercase names?
#
# Also, notice how there's no spaces between variable and value...
# 'var = val' is different than 'var=val'... the former won't work
# for assigning a value to a variable!
name=Santiago
last_name=Alvarez

echo name last_name
~/Document $ ./my_first_script.sh
name last_name
#!/usr/bin/env bash

name=Santiago
last_name=Alvarez

# To access a variable's value, you must use '$' before the name
echo $name $last_name
~/Document $ ./my_first_script.sh
Santiago Alvarez

# Now we're talking!
#!/usr/bin/env bash

name=Santiago
last_name=Alvarez

echo $name $last_name

# Let's try using some ENV
echo 'My home dir is: $HOME'
~/Document $ ./my_first_script.sh
Santiago Alvarez
My home dir is: $HOME

# You're joking, right?
#!/usr/bin/env bash

# Without the !, this line is just a comment

# Remember what I told you about lowercase/uppercase names?
name=Santiago
last_name=Alvarez

# To access a variable's value, you must use '$' before the name
echo $name $last_name

# Let's try using some ENV
echo "My home dir is: $HOME"
~/Document $ ./my_first_script.sh
Santiago Alvarez
My home dir is: /home/santiaro90

That's word expansion going on... If you use single quotes, Bash takes the string as literal. Double quotes make Bash expand variables and split words(*)

(*) See more on quoting and word splitting in the links at the end of the presentation

Hey, that's boring! I wanna do cool stuff... You promised me!

#!/usr/bin/env bash

# Save this to ~/Documents/sloc_count.sh
# Recall our exercise on find + grep
# Let's find how many SLOC are there in ruby files

blank_regex='^\s*$'
comment_regex='^\s*#'
exclude_pattern='*/test/*'

# '\' to split command in multiple lines
# You can wrap a command in '$()' to assign its result to a variable
count=$(find "$PWD" -name '*.rb' -not -path "$exclude_pattern" | \
    xargs grep -vE -e "$blank_regex" -e "$comment_regex" | \
    wc -l)

echo 'Ruby SLOC for project in current directory:'
echo $count
~/Documents $ chmod u+x sloc_count.sh

~/Documents $ cd rails

~/Documents $ ~/Documents/sloc_count.sh
Ruby SLOC for project in current directory:
65616

(crossing fingers...)

Some Links

Made with Slides.com