The Only Shell Guide that I Need
This was written at my very woke moments when I realize I'm probably going to forget about some tiny little details about bash shell if I don't use all of them that much in the future. And also, sometimes, snippets can be handy.
Basics in case I have brain damage
These are some very basic stuffs, in case you really need them.
Shebang!
It's a common thing to add headers (shebang
in nature) to shell script to designate the shell you wanna use. You can go with sh
or bash
or whatever works.
#!/bin/sh
#!/bin/bash
It can have parameters:
#!/bin/sh +x
Even with PHP script, though it's off topic.
#!/usr/bin/env php
Let there be variables
Everything is "string".
You wanna have literals? Gotcha covered.
# No space after variable name or after the equal sign.
# And yes, this is a string, not an integer or number or anything,
a=123
# which means this is the very same command as:
a="123"
# For strings having spaces or special characters that need to be
# escaped, you gotta quote them
a="I know, I know\nWhat's next?"
Pass a variable? No problem.
# Yes, you gotta quote them
b="$a"
# Along with other literals
b="a is like: $a"
# In case `a` doesn't have a space around it
b="How many ${a}s do you have again?"
You can even use an output of a command? Coooool!
a="Today is $(date +%Y-%d-%m)"
if
Well, about if
control flow, shell has quite a bit of history.
So, there are a lot of ways to write conditions, namely []
, test
command, [[]]
, let
and (())
.
Generally, it is written in this manner:
if CONDITION
then
# Do something
fi
# You can have condition and `then` at the same line
if CONDITION ; then
# Do something
fi
# You can also have an `else` section
if CONDITION ; then
# Do A
else
# Do B
fi
As for how condition is written, that's where the "goodies" are.
[]
or test
# How spaces are put is very strict here
# `$a = "123"` is totally different from `$a="123"`
# where `$a="123"` without spaces would be treated
# as a string as a whole instead of testing whether
# $a and 123 are equal
if [ $a = "123" ]
# For bash you can:
if [ $a == "123" ]
# `[]` is pretty much the same as `test`
if test $a = "123"
# if $a and 123 are not equal
if [ $a != "123" ]
# If $a is an empty string
if [ -z $a ]
# If $a is NOT an empty string
if [ -n $a ]
# or
if [ ! -z $a ]
# Numeric comparison notations like "<", ">", ">=", "<="
# are not directly supported, you have to do these instead:
# If $a equals to $b
if [ $a -eq $b]
# If $a and $b are not equal
if [ $a -ne $b]
# If $a is greater than $b
if [ $a -gt $b]
# If $a is greater or equal than $b
if [ $a -ge $b]
# If $a is less than $b
if [ $a -lt $b]
# If $a is less or equal than $b
if [ $a -le $b]
# For logic operators, you have to use these instead:
# If $a equals 1 and $b equals 2
if [ $a = 1 -a $b = 2 ] # Note this does NOT short circuit
# or
if [ $a = 1 ] && [ $b = 2 ] # Note this does short circuit
# If $a equals 1 or $b equals 2
if [ $a = 1 -o $b = 2 ] # Again, this does NOT short circuit
# or
if [ $a = 1 ] || [ $b = 2 ] # This does short circuit
# If `$dir` is a directory path that exists
if [ -d "$dir"]
# If `$file_path` is not a file path that exists
if [ ! -f "$file_path"]
[[]]
is not very universal, you could probably only use it with Bash or ksh. It was introduced into Bash at the version 2.02. But it is much more capable than []
in terms of expressiveness.
# You can use wildcards here. But still, spaces are very
# important
if [[ $a = 12* ]] # will match `123`, `12f2`, `12dsa`...
if [[ $a = 12? ]] # will match `123`, `12f`, `12w`...
# If you mean star literally, you should quote it so that
# it won't be treated as a wildcard
if [[ $a = "12*" ]] # will only match `12*`
# You can compare using normal math notations here
if [[ $a > $b ]]
# You can use logic operators inside here
if [[ $a = 1 && $b = 2 ]] # This does short circuit evaluation
# or
if [[ $a = 1 ]] && [[ $a = 2 ]] # This also does short circuit evaluation
(())
is basically doing arithmetic calculations, but its result can be used as a logic value for if
control flow.
# In `(())`, you can omit dollar sign for variables
# and also, spaces in the `(())` are not very strict
if ((a==10))
# Note that a single equal sign `=` means assignment here
if ((a=1)) # This will always be true and $a will become 1
# You can do these:
if ((a>6 && b<10))
if ((a/3==4))
# Pretty impressive, no?
while
loops
If you understand how conditions are written for if
statement, then while
should be no problem for you. There is also another thing called until
loops, which work very much like while
loops, the only difference being until
loops only run its loops when CONDITION are NOT met.
while CONDITION
do
# Repeatedly do something
done
# A very simple example
while ((i <= 100))
do
((sum += i))
((i++))
done
# Same thing but using until
until ((i > 100))
do
((sum += i))
((i++))
done
# You can do `continue` and `break`
until ((i > 100))
do
((sum += i))
((i++))
if ((i==40)) ; then
break
else
continue
fi
done
for
loops
for
loops have two styles, one like C/C++, the other like python. You can also do break
and continue
by the way.
C style for
loop
for ((i=1; i<=100; i++))
do
((sum += i))
done
Python style for
loop
# List values
for n in 1 2 3 4 5 6
do
echo $n
((sum+=n))
done
# Can be strings. (Numbers are also strings actually)
for str in "a" "b" "c"
do
echo $str
done
# Can be a range
for n in {1..100}
do
((sum+=n))
done
# Or range of characters (in the order of ASCII codes)
for c in {A..z}
do
echo $c # This will include []^_`
done
# Can also use the result of a commmand
for filename in $(ls *.sh)
do
echo $filename
done
Unix special variable list
Variable | Description |
$0 | The filename of the current script. |
$n | These variables correspond to the arguments with which a script was invoked. Here n is a positive decimal number corresponding to the position of an argument (the first argument is $1, the second argument is $2, and so on). |
$# | The number of arguments supplied to a script. |
$* | All the arguments, all double quoted. If a script receives two arguments, $* is equivalent to $1 $2. |
$@ | All the arguments, all individually double quoted. If a script receives two arguments, $@ is equivalent to $1 $2. |
$? | The exit status of the last command executed. |
$$ | The process number of the current shell. For shell scripts, this is the process ID under which they are executing. |
$! | The process number of the last background command. |
Snippets
Get script path
shell_dir=$(dirname "$0")
Delete a directory if exits
if [ -d "$dir" ] ; then
rm -rf "$dir"
fi
Create a directory if it doesn't exist
if [ ! -d "$dir" ] ; then
mkdir -p "$dir"
fi