Shell Language Syntax

Table of Contents

Arrays reference

Declare an array variable
Usage

Iterate array elements howto

arr=(one two three)
if [[ "${#arr[@]}" -gt 0 ]]; then
  for n in "${arr[@]}"; do
    echo "$n"
  done
fi

Join array elements howto

readonly X=('foo' 'bar' 'baz')
readonly Y="$(IFS=','; echo "${X[*]}")"
echo "${Y}"
foo,bar,baz

case reference

case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  *)
    echo $"Usage: $0 {start|stop}"
    exit 1
    ;;
esac

for reference

for i in 1 2 3 4 5; do
  echo "$i"
done

for file in ~/repos/* ; do
  echo "$file"
done

# continue and break
for i in 1 2 3; do
  if [[ "$i" == 1 ]]; then
    continue
  fi
  if [[ "$i" == 3 ]]; then
    break
  fi
  echo "$i"
done

for (( i=1; i<=5; i++)); do
  echo "$i"
done
# requires bash v3.0+
for i in {1..5}; do
  echo "$i"
done

# requires bash v4.0+
for i in {0..10..2}; do
  echo "$i"
done

if reference

if commands; then
  commands
[elif commands; then
  commands ...]
[else
  commands]
fi
[ -a FILE ] True if FILE exists.
[ -b FILE ] True if FILE exists and is a block-special file.
[ -c FILE ] True if FILE exists and is a character-special file.
[ -d FILE ] True if FILE exists and is a directory.
[ -e FILE ] True if FILE exists.
[ -f FILE ] True if FILE exists and is a regular file.
[ -g FILE ] True if FILE exists and its SGID bit is set.
[ -h FILE ] True if FILE exists and is a symbolic link.
[ -k FILE ] True if FILE exists and its sticky bit is set.
[ -p FILE ] True if FILE exists and is a named pipe (FIFO).
[ -r FILE ] True if FILE exists and is readable.
[ -s FILE ] True if FILE exists and has a size greater than zero.
[ -t FD ] True if file descriptor FD is open and refers to a terminal.
[ -u FILE ] True if FILE exists and its SUID (set user ID) bit is set.
[ -w FILE ] True if FILE exists and is writable.
[ -x FILE ] True if FILE exists and is executable.
[ -O FILE ] True if FILE exists and is owned by the effective user ID.
[ -G FILE ] True if FILE exists and is owned by the effective group ID.
[ -L FILE ] True if FILE exists and is a symbolic link.
[ -N FILE ] True if FILE exists and has been modified since it was last read.
[ -S FILE ] True if FILE exists and is a socket.
[ FILE1 -nt FILE2 ] True if FILE1 is newer than FILE2, or if FILE1 exists and FILE2 does not.
[ FILE1 -ot FILE2 ] True if FILE1 is older than FILE2, or is FILE2 exists and FILE1 does not.
[ FILE1 -ef FILE2 ] True if FILE1 and FILE2 refer to the same device and inode numbers.
[ -o OPTIONNAME ] True if shell option "OPTIONNAME" is enabled.
[ -z STRING ] True of the length if "STRING" is zero.
[ -n STRING ] True if the length of "STRING" is non-zero.
[ STRING ] True if the length of "STRING" is non-zero.
[ STRING1 == STRING2 ] True if the strings are equal.
[ STRING1 != STRING2 ] True if the strings are not equal.
[ STRING1 < STRING2 ] True if "STRING1" sorts before "STRING2"
[ STRING1 > STRING2 ] True if "STRING1" sorts after "STRING2"
[ ARG1 OP ARG2 ] "OP" is one of -eq, -ne, -lt, -le, -gt or -ge.
[ ! EXPR ] True if EXPR is false.
[ ( EXPR ) ] Returns the value of EXPR. To override the normal precedence of operators.
[ EXPR1 -a EXPR2 ] True if both EXPR1 and EXPR2 are true.
[ EXPR1 -o EXPR2 ] True if either EXPR1 or EXPR2 is true.
if [[ -z "$foo" ]] && [[ -z "$bar" ]];
if [[ -z "$foo" && -z "$bar" ]]; # equivalent to above

function reference

print_something() {
    echo Hello $1
}
print_something Mars
print_something Jupiter
# Single function
my_func() {
    ...
}

# Part of a package
mypackage::my_func() {
    ...
}
#######################################
# Cleanup files from the backup dir
# Globals:
#   BACKUP_DIR
#   ORACLE_SID
# Arguments:
#   None
# Returns:
#   None
#######################################
cleanup() {
    ...
}
# If N is omitted, the return status is that of the
# last command executed within the function or script.
return [n]

Note that if you have set -e set at the top of your script and your return 1 or any other number besides 0, your entire script will exit. exit abandons the current shell.

var_change() {
    local var1='local 1'
    echo Inside function: var1 is $var1 : var2 is $var2
    var1='changed again'
    var2='2 changed again'
}
var1='global 1'
var2='global 2'
# only var2 changed
foo() {
  return 0 # return returns a value from a function.
}
bar() {
  exit 1 # exit abandons the current shell.
}

foo
echo 'hi'
bar
echo 'bye' # NOT printed

Here document reference

tr a-z A-Z << END_TEXT
one two three
four five six
END_TEXT
ONE TWO THREE
FOUR FIVE SIX
# Ignore leading tabs
tr a-z A-Z <<- END_TEXT
         one two three
         four five six
         END_TEXT
(Same as above)
# Disable string interpolation
cat << 'EOF'
\$ Working dir "$PWD" `pwd`
EOF
\$ Working dir "$PWD" `pwd`
cat <<EOF | sh
touch somefile
echo foo > somefile
EOF
(
cat <<EOF
touch somefile
echo foo > somefile
EOF
) | sh
{
cat <<EOF
touch somefile
echo foo > somefile
EOF
} | sh
cat >out <<EOF
test
EOF

Here strings reference

$ tr a-z A-Z <<< one
ONE

$ FOO='one two three'
$ tr a-z A-Z <<< $FOO
ONE TWO THREE
$ echo 'one two three' | read a b c
$ echo $a $b $c
# yields nothing, because 'read' ran on subshell

$ read a b c <<< 'one two three'
$ echo $a $b $c
one two three

Shebang(#!/usr/bin) reference

Use #!/usr/bin/env bash for portability
*Different nixes put bash in different places, and using /usr/bin/env is a workaround to run the first bash found on the PATH.

Escaping quotes discussion

Escaping double quotes

"\""
$ echo 'echo $#' > arg-count
$ chmod +x arg-count
$ ./arg-count
0
$ echo "$(./arg-count $(echo foo bar))"
2
$ echo "$(./arg-count "$(echo foo bar)")"
1

Escaping single quotes within a single quoted string

alias rxvt='urxvt -fg '"'"'#111111'"'"' -bg '"'"'#111111'"'"
#                     ^^^^^       ^^^^^     ^^^^^       ^^^^
#                     12345       12345     12345       1234

  1. ' End first quotation which uses single quotes.
  2. " Start second quotation, using double-quotes.
  3. ' Quoted character.
  4. " End second quotation, using double-quotes.
  5. ' Start third quotation, using single quotes.

Or, use ANSI C string:($''). We can escape a single quote with \'. But in this way, we loses bash's literal meaning. Other meta character like \n, \t will also get a special meaning.

echo $'Can\'t do that'

Usage of dash (-) in place of a filename, like cat - discussion

Using - as a filename to mean stdin / stdout is a convention that a lot of programs use. It is not a special property of the filename. If you want to use a file named as -, you should pass the argument like ./-.