Santiago Álvarez Rodríguez
Software engineer, front-end developer and language learner.
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!
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. |
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.
#!/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...)
By Santiago Álvarez Rodríguez
A first sight at how to automate all those nice commands you've learnt so far. Environment variables, local variables, script parameters and more.