Shell

Bash

Bourne Again SHell, is also a language

  • How to run a bash script:
    • bash /path/to/script
    • chmod +x /path/to/script then /path/to/script (requires shebang)

Script

Parameter

  • $1~$9, ${10}…: Parameters
  • $@: All parameters (independent)
  • $*: All parameters (catenate as one string)
  • $#: The number of parameters
  • $?: Return value of last command
  • $$: PID of this shell
  • $!: PID of latest background process
  • $_: The last parameter of last command
  • $-: This shell’s options

Comments

  • Comments: Starts with ”#”
  • Shebang: A special comment that specifies that the file is a script and calls a certain interpreter, e.g. bash, python.
    • #!/bin/bash
    • #!/usr/bin/python

Variable

NAME="value"
echo "$NAME"
  • Whitespace matters! NO SPACE!!!
  • Use $VAR, or ${VAR} (preferred) to output the value of the variable VAR
  • Variable in bash is untyped
  • Operations are contextual
  • Everything is string

Use expr command or $(()) (better way) to evaluate expressions.

FOO=1
expr $FOO + 1         # 2
expr $FOO+1           # 1+1
echo $((FOO + 1))   # 2
echo $((FOO+1))     # 2

Use the read command to get user input, -p is for optional prompt (Not supported by zsh, use echo -n instead)

read -p "send: " FOO
echo "send: $FOO"

Subshell: Use $() evaluate the command inside it and substitute the output into the script.

BAR=$(ls)
echo "$BAR"

Conditional

test, synonymous with [], could evaluate an expression and set exit status ($?) to:

  • 1 (false)
  • 0 (true)

Comparison:

  • -eq: ==
  • -ne: !=
  • -gt: >
  • -lt: <
  • -ge: >=
  • -le: <=
a=10
b=2
 
[ "$a" -le "$b" ]        # false
[[ "$a" <= "$b" ]]      # true (String)
(( a <= b ))            # false

Note

  • [ ... ]: A command: POSIX test, the oldest one.
  • [[ ... ]]: Modern shell condition keyword, support regex (=~)
  • (( ... )): Expression evaluation, $? will be set as 0 (true) if outcome is not 0

Boolean operations:

  • && and || for shell
  • -a (and) and -o (or) for test ([])
false || echo "Oops, fail"
# Oops, fail
 
true || echo "Will not be printed"
#
 
true && echo "Things went well"
# Things went well
 
false && echo "Will not be printed"
#
 
true ; echo "This will always run"
# This will always run
 
false ; echo "This will always run"
# This will always run

(false is a command always returning 1 and true is a command always return 0)

[ 0 -le 1 ] && [ 0 -gt 1 ]; echo $?
# 1
[ 0 -le 1 -o  0 -gt 1 ]; echo $?
# 0 

if statement:

if [ $1 -lt 0 ]; then
  echo "$1 is less than 0"
elif [ $1 -eq 0 ]; then
  echo "$1 is equal to 0"
else
  echo "$1 is greater than 0"
fi

case statement: Use to pattern match

case $1 in
  Mon|Tue|Wed|Thu|Fri)
    echo "Weekday..."
    ;;
  Sat|Sun)
    echo "Weekend!!!"
    ;;
  *)
    echo "Invaild"
esac

Loops

Array: my_array=(apple banana cherry)

for loop:

SHEEP=("one" "dos" "tre")
for S in $SHEEP; do
  echo "$S sheep..."
done

In fact, for can also traverse a string with $IFS as delimiter.

  • With index:
n=0
for x in {1..10}; do
  n=$(($x + $n))
done
echo $n

while loop:

while true; do
  echo "yes"
done

To read a file by lines:

while IFS= read -r line; do
  echo "$line"
done < input.txt

Note

VAR=value command arguments
VAR will only affect the command behind it.

Function

function greet() { # () is optional.
  echo "Hello, $1!"
}
greet "World"

Functions has its own environment but a subtle difference: $1 in function will be the argument passed in the function, not the global one (the argument passed in the script), even if function is called without argument.

  • function is a keyword not supported by POSIX which could return an integer and use $FUNCNAME access function’s name, but without function keyword, () is required.
  • local can declare a variable in the field of function.

Stream

  • >: Output to somewhere else, like a text file. (If there’s anything in the file before, it will be overwritten!)

    • echo "Hello, Wolrd!" > out.txt
  • >>: Append output to a file. It works same as > if file is empty or not exist.

    • echo "Goodbye, Wolrd!" >> out.txt
  • <: Take input from a file

    • cmd < in.txt
  • Pipes (|): Take output of first command as the input of second command, connecting stdin and stdout.

    • cmd1 | cmd2
    • It will create a subshell for every command!
  • Process substitution (<(command)): Simulate the output of command as a file, so it will not create subshell.

    • Place the output in a temporary file and substitute the <() with that file’s name.
    • Handy when a command need a file as argument or input from files instead of stdin.
  • stdin: 0

  • stdout: 1

  • stderr: 2

Redirection: 2>&1, redirecting stderr to stdout (If stdout is also redirected, stderr will also redirect to where stdout is redirected to). So sequence matters.

E.g.

ping -c google.com > /dev/null 2>&1

stdout /dev/null; stderr stdout ( /dev/null)

ping -c google.com 2>&1 > /dev/null

stderr stdout terminal stdout /dev/null

Some common situation

  • Discard stdout: command > /dev/null
  • Discard stderr: command 2> /dev/null
  • Discard both: command > /dev/null 2>&1
  • Output together (and use pipe to deal with): command 2>&1 | grep error

Useful Tools/Commands

  • sed
  • awk