Bash Scripting: Difference between revisions

From Leo's Notes
This page was last edited on 16 September 2022, at 23:53.
mNo edit summary
 
(46 intermediate revisions by 2 users not shown)
Line 1: Line 1:
== Common Functions ==
==Useful functions==
Here are some common functions I use in my scripts.
Here are some common functions and commands I use in my scripts.
{| class="wikitable"
!Function
!Code
|-
|Use this to ensure the script runs relative to its location.


=== Prompt ===
This is useful if your script uses relative paths.
You can prompt the user for an yes or no answer using this function. An optional default values are accepted as the second argument.
|{{highlight|lang=bash|code=
#!/bin/bash
cd "$(dirname "$0")"
}}
|-
|Find the maximum of two numbers. You can implement min by flipping the operands.
|{{highlight|lang=bash|code=
max (){
        echo $(($1>$2 ? $1 : $2))
<nowiki>}</nowiki>
}}
|-
|Similar to the {{code|die}} command in PHP which prints out a message before stopping execution.


Example Usage:
'''Note that this will not work inside a subshell''' as it will just quit the subshell rather than the script. To get around this, you may need to kill the parent PID instead.
<syntaxhighlight lang="bash">
|<syntaxhighlight lang="bash">
if prompt "Is this okay?" Y ; then
function die {
    echo "It's Okay!"
echo $@
fi
exit
if ! prompt "Is this okay?" N ; then
}
    echo "It's not okay!"
fi
</syntaxhighlight>
</syntaxhighlight>
|-
|Indents text piped to the function.
|{{highlight|lang=terminal|code=
# Supports up to 10 tabs
function Indent (){
Tabs="\t\t\t\t\t\t\t\t\t\t"
sed "s/^/${Tabs:0:$((2*$1))}/g"
}
echo "test" {{!}} Indent 1
test
echo "test" {{!}} Indent 2
test
}}
|-
|Extracting the file name and file extension from a path
|{{highlight|lang=bash|code=
Path="/etc/resolv.conf"
# Get path parts
Directory=$(dirname $Path)  # /etc
Filename=$(basename $Path)  # resolv.conf
# Get filename parts
Name="${Filename%.*}"  # resolv
Ext="${Filename##*.}"  # conf
}}
|-
|Skip the first N lines from an output
|{{highlight|lang=terminal|code=
## -n +N outputs starting on line N.
## Eg. To skip the first 2 lines, we start on line 3:
# tail -n+3
}}
|-
|Get the current Unix timestamp
|{{Highlight
| code = $ date +"%s"
1596137957
| lang = terminal
}}
|-
|Unix timestamp to human readable time.
You can use either <code>awk</code> with <code>strftime</code>, or with <code>date</code>.
|{{highlight|lang=terminal|code=
$ echo 1355514777 {{!}} awk '{ print strftime("%c", $1) }'
Fri 14 Dec 2012 12:52:57 PM MST
}}{{Highlight
| code = $ date +"%c" -d @15996137957
Thu 30 Jul 2020 01:39:17 PM MDT
| lang = terminal
}}
|-
|Get a file's last modified time in Unix timestamp
|<code>stat -c %Y ''filename''</code>
|-
|Convert a multi-line list into a comma delimited string
|<code>''cat list''<nowiki> | paste -s -d, -</nowiki></code>
|-
|Get all IPv4 addresses on this host
|<code><nowiki>ip a | grep -ohE 'inet [0-9.]{7,15}'</nowiki></code>
|-
|Get the URL from <a href="...">
|<code>''echo '<a href="asdf">'<nowiki/>''<nowiki> | grep -Eo 'href="[^\"]+"' | sed 's/href="\(.*\)"/\1/'</nowiki></code>
|-
|Replace substring in Bash
|
{| class="wikitable"
|-
|Use <code>${variable/search_term/replace_term}</code>. Eg:{{highlight|lang=bash|code=
Path="/etc/resolv.conf"
# This returns /etc/hostname
echo ${Path/resolv.conf/hostname<nowiki>}</nowiki>
}}
|}
|}
==Useful scripting patterns==
===Get a specific column===
You can use either <code>awk</code> with delimiters specified with <code>-F</code>.
{{Highlight
| code = $ echo one two three four five {{!}} awk '{print $3}'
three
$ echo one,two,three,four,five {{!}} awk -F, '{print $3}'
three
| lang = terminal
}}
Or cut by a specific delimiter and select a specific column. This method cannot handle columns separated by more than one space. If you have neat columns, you'll need to pipe through <code>tr -s ' '</code> first which collapses (or squeezes) the given repeating characters down.
{{Highlight
| code = $ echo one two three four five {{!}} cut -d ' ' -f3
three
$ echo "one    two    three    four    five" {{!}} tr -s ' ' {{!}} cut -d ' ' -f3
three
$ echo one,two,three,four,five {{!}} cut -d ',' -f3
three
| lang = terminal
}}Or use the bash built-in function <code>read</code> which can take in a list of variables. Variable assignment by piping to <code>read</code> works in zsh but will only work if used within a subshell in bash. Keep this behavior in mind if you choose to use this method of separating out words in a string. For example:{{Highlight
| code = ## Basic variable assignment with read
$ echo one two three four five {{!}} while read _ second _; do echo $second ; done
two


Function:
## zsh variable assignment works without a subshell/loop
<syntaxhighlight lang="bash">
% echo one two three four five {{!}} read _ second _
function prompt() {
% echo $second
# credit: http://djm.me/ask
two
while true; do
| lang = terminal
if [ "${2:-}" = "Y" ]; then
}}The last variable contains all remaining columns. Unwanted columns can be named repeatedly with <code>_</code> (like in golang) and ignored.{{Highlight
prompt="Y/n"
| code = ## Remember, to get only the 3rd column, we need to read the 4th column out.
default=Y
% echo one two three four five {{!}} read _ _ third; echo $third
elif [ "${2:-}" = "N" ]; then
three four five
prompt="y/N"
% echo one two three four five {{!}} read _ _ third _ ; echo $third
default=N
three
else
prompt="y/n"
default=
fi


# Ask the question
## Delimiters specified with IFS
read -p "$1 [$prompt] " REPLY
% echo one,two,three,four,five {{!}} IFS=',' read _ _ third _ ; echo $third
three
| lang = terminal
}}In terms of speed, be aware that using <code>awk</code> or <code>tr+cut</code> are actual commands that spawns a sub-process whereas read is a bash built-in function. As a result, it's always faster to use read to parse out data, especially if in a loop.  If you are looping and using <code>awk</code> or <code>cut</code>, perhaps consider replacing it with the bash <code>read</code> equivalent. For example:
{{Highlight
| code = ## Super slow! Invokes awk 2 times per line
$ w {{!}} tail -n +3 {{!}} while read i ; do
  Username=$(echo "$i" {{!}} awk '{print $1}')
  Source=$(echo "$i" {{!}} awk '{print $3}')
  echo $Username $Source
done


# Default?
## Instead do something like this!
if [ -z "$REPLY" ]; then
$ w {{!}} tail -n +3 {{!}} while read Username _ Source _ ; do
REPLY=$default
  echo $Username $Source
fi
done
| lang = terminal
}}


# Check if the reply is valid
===Preserve header while sorting without using a temporary file===
case "$REPLY" in
You can sort an output on a specific column while preserving the header and without needing a temporary file by using a shim function that reads and prints the first line before passing the rest of the output down the pipe. Define this function:
Y*|y*) return 0 ;;
{{Highlight
N*|n*) return 1 ;;
| code = # print the header (the first line of input)
esac
# and then run the specified command on the body (the rest of the input)
done
# use it in a pipeline, e.g. ps {{!}} body grep somepattern
body() {
    IFS= read -r header
    printf '%s\n' "$header"
    "$@"
}
}
</syntaxhighlight>
| lang = bash
}}
Then use this shim body function with the desired sort function. For example:
{{Highlight
| code = $ top -b -n 1 {{!}} head -n 15 {{!}} tail -n +7 \
  {{!}} body sort -k 5 -rn
| lang = terminal
}}
Credit to https://unix.stackexchange.com/questions/11856/sort-but-keep-header-line-at-the-top for this neat trick.
==Testing things in Bash==
{{code|test}} is a bash built-in function to add conditionals in your script. {{code|[}} is an alias of the {{code|test}} command and are analogous.
 
Use the test operators listed below with {{code|test}} or a single bracket test condition like the following example.
{{highlight|lang=terminal|code=
$ test -e /etc/hosts && echo "File exists"
File exists
 
## Note that the white space around [ and ] and also the operator are required.
$ [ -e /etc/hosts ] && echo "File exists"
File exists
 
$ if [ -e /etc/hosts ] ; then echo "File exists" ; fi
File exists
}}
 
{| class="wikitable"
! width="125px" |Operator
!Description
|-
!{{code|-e}}
|file exists
|
|-
!{{code|-a}}
|file exists (deprecated)
 
This is identical in effect to {{code|-e}}
|-
!{{code|-f}}
|file is a regular file (not a directory or device file)
|-
!{{code|-s}}
|file is not zero size
|-
!{{code|-d}}
|file is a directory
|-
!{{code|-b}}
|file is a block device. Eg. {{code|/dev/sda}}.
|-
!{{code|-c}}
|file is a character device. Eg. {{code|/dev/tty0}}.
|-
!{{code|-p}}
|file is a pipe. Eg. {{code|/dev/fd/0}}.
|-
!{{code|-h}}
|file is a symbolic link
|-
!{{code|-L}}
|file is a symbolic link
|-
!{{code|-S}}
|file is a socket
|-
!{{code|-t}}
|file (descriptor) is associated with a terminal device
 
This test option may be used to check whether the stdin [ -t 0 ] or stdout [ -t 1 ] in a given script is a terminal.
|-
!{{code|-r}}
|file has read permission (for the user running the test)
|-
!{{code|-w}}
|file has write permission (for the user running the test)
|-
!{{code|-x}}
|file has execute permission (for the user running the test)
|-
!{{code|-g}}
|file or directory has set-group-id (sgid) flag set
 
If a directory has the sgid flag set, then a file created within that directory belongs to the group that owns the directory.
|-
!{{code|-u}}
|file has set-user-id (suid) flag set
 
|-
!{{code|-k}}
|file or directory has sticky bit set
 
*If set on a file, that file will be kept in cache memory, for quicker access.
*If set on a directory, it restricts write permission. Setting the sticky bit adds a t to the permissions on the file or directory listing. This restricts altering or deleting specific files in that directory to the owner of those files.
|-
!{{code|-O}}
|you are owner of file
|-
!{{code|-G}}
|group-id of file same as yours
|-
!{{code|-N}}
|file modified since it was last read
|-
!{{code|f1 -nt f2}}
|file f1 is newer than f2
|-
!{{code|f1 -ot f2}}
|file f1 is older than f2
|-
!{{code|f1 -ef f2}}
|files f1 and f2 are hard links to the same file
|-
!{{code|!}}
|"not" -- reverses the sense of the tests above (returns true if condition absent).
|}
 
===Integers===
 
Integer tests, with {{code|test}} or inside single brackets.
 
Eg.
{{highlight|lang=terminal|code=
$ test 2 -eq 2 && echo equal || echo not equal
equal
 
$ [ 2 -eq 2 ] && echo equal || echo not equal
equal
}}
 
 
{| class="wikitable"
! width="125px" |Operator
!Description
|-
!{{code|-eq}}
|is equal to
|-
|-
!{{code|-ne}}
|is not equal to
|-
!{{code|-gt}}
|is greater than
|-
!{{code|-ge}}
|is greater than or equal to
|-
!{{code|-lt}}
|is less than
|-
!{{code|-le}}
|is less than or equal to
|}
 
 
 
Integer tests can also be done inside double parentheses. Eg.
{{highlight|lang=terminal|code=
$ ((2==2)) && echo equal || echo not equal
equal
}}
 
 
{| class="wikitable"
! width="125px" |Operator
!Description
|-
!{{code|1===}}
|is equal
|-
!{{code|<}}
|is less than
|-
!{{code|1=<=}}
|is less than or equal to
|-
!{{code|>}}
|is greater than
|-
!{{code|1=>=}}
|is greater than or equal to
|}
 
 
===Strings===


=== die ===
{| class="wikitable"
Similar to the <code>die</code> command in PHP which prints out a message before stopping execution.
! width="125px" |Operator
!Description
|-
!{{code|-z}}
|string is null, that is, has zero length
|-
!{{code|-n}}
|string is not null.
|-
!{{code|1==}}
|is equal to


Function:
{{highlight|lang=terminal|code=
<syntaxhighlight lang="bash">
## Does not work in zsh. Works only in sh and bash.
function die {
$ if [ "$a" = "$b" ] ; then
echo $@
  echo "Equal"
exit
fi
}
}}
</syntaxhighlight>
|-
!{{code|1===}}
|is equal to


The caveat is that exit will kill the current shell - if running in a subshell, this will terminate the subshell and not the parent shell. To get around this, you may need to kill the parent PID instead.
{{highlight|lang=terminal|code=
## Does not work in zsh. Works only in sh and bash.
$ if [ "$a" == "$b" ] ; then
  echo "Equal"
fi
}}


=== Indent ===
This operator inside a double bracket {{code|[[ ]]}} construct does pattern matching.
Indents text piped to the function.
|-
!{{code|1=!=}}
|is not equal to


Function:
{{highlight|lang=terminal|code=
<syntaxhighlight lang="bash">
$ if [ "$a" != "$b" ] ; then
function Indent {                                                                                                   
  echo "Not equal"
Prepend="\t"
fi
}}


if [ $# -eq 1 ] ; then
This operator inside a double bracket {{code|[[ ]]}} construct does pattern matching.
Indent=$1
|-
!{{code|1==~}}
|Match using regex


while [ $Indent -ge 0 ] ; do
{{highlight|lang=terminal|code=
Prepend="\t$Prepend"
$ if [ zone36-ta =~ .*ta$ ] ; then
Indent=$(($Indent - 1))
  echo "matches"
done
fi
fi


while read i ; do echo -e "${Prepend}$i" ; done
$ if [ zone36-ta =~ *ta ] ; then
}
  echo "matches"
</syntaxhighlight>
fi
}}
|}<br />


Usage:
==Command Substitution==
<syntaxhighlight lang="bash">
Command substitution allows output from a command to be substituted in place of the command itself in a script. Commands can be enclosed within backticks {{code|`command`}} or the POSIX compatible form {{code|$(command)}}.
echo "test" | Indent
{{highlight|lang=terminal|code=
test
## Eg.
echo "test" | Indent 2
echo "Today is " $(date)
test
echo "Today is " `date`
</syntaxhighlight>
}}


== Extracting / Parsing Filenames ==
Do not confuse {{code|$(command)}} with {{code|$((expression))}} which is used for arithmetic expansion.
Use the <code>basename</code> to extract filenames from a path and and <code>dirname</code> to extract the path of a filename.


Eg:
===Nested Substitution===
dirname /etc/hosts
If you are trying to do something similar to:
/etc
{{highlight|lang=bash|code=
Something=`basename `ls /home/*.txt``
}}


  basename /etc/hosts
You will get an error since the expansion is ambiguous. Instead, use the {{code|$(command)}} substitution instead:
hosts
{{highlight|lang=bash|code=
Something=$(basename $(ls /home/*.txt))
}}


To extract only the extension or the filename without the extension, use bash's string matching:
Filename=$(basename /etc/resolv.conf)
Ext="${Filename##*.}"
Name="${Filename%.*}"


== Arithmetic Operations ==
==Arithmetic Operations==
Do integer arithmetic operations on variables within double brackets. For example:
Do integer arithmetic operations on variables within double brackets. For example:
Value=1
{{highlight|lang=terminal|code=
ValuePlusOne=$((Value + 1))
Value=1
ValueTimes12=$((Value * 12))
ValuePlusOne=$((Value + 1))
ValueTimes12=$((Value * 12))
}}


The operators that are supported are:
The operators that are supported are:
  + - / * %  
  + - / * %  
Bitwise operators also work:
Bitwise operators also work:
  ^ (XOR)  | (OR)  & (AND)
  ^ (XOR)  | (OR)  & (AND)  << (Shift left)  >> (Shift right)


 
==Control Blocks==
== Control Blocks ==
===Switch===
=== Switch ===
{{highlight|lang=terminal|code=
<syntaxhighlight lang="bash">
read -p "What do you want to do?: " Option
read -p "What do you want to do?: " Option


Line 134: Line 474:
;;
;;
esac
esac
</syntaxhighlight>
}}
 
==See Also==


== Snippets ==
*https://www.tldp.org/LDP/abs/html/comparison-ops.html
=== Get First IP Address ===
*http://sipb.mit.edu/doc/safe-shell/
ip a | /bin/grep inet | grep -v inet6 | tail -n+2 | head -n 1 | awk '{print $2}' | cut -d'/' -f1
*http://mywiki.wooledge.org/BashFAQ
*https://github.com/dylanaraps/pure-sh-bible


{{Navbox Linux}}
[[Category:Linux]]
[[Category:Linux]]
{{Navbox Linux}}

Latest revision as of 23:53, 16 September 2022

Useful functions

Here are some common functions and commands I use in my scripts.

Function Code
Use this to ensure the script runs relative to its location.

This is useful if your script uses relative paths.

#!/bin/bash
cd "$(dirname "$0")"
Find the maximum of two numbers. You can implement min by flipping the operands.
max (){
        echo $(($1>$2 ? $1 : $2))
}
Similar to the die command in PHP which prints out a message before stopping execution.

Note that this will not work inside a subshell as it will just quit the subshell rather than the script. To get around this, you may need to kill the parent PID instead.

function die {
	echo $@
	exit
}
Indents text piped to the function.
# Supports up to 10 tabs
function Indent (){
	Tabs="\t\t\t\t\t\t\t\t\t\t"
	sed "s/^/${Tabs:0:$((2*$1))}/g"
}

echo "test" | Indent 1
	test
echo "test" | Indent 2
		test
Extracting the file name and file extension from a path
Path="/etc/resolv.conf"

# Get path parts
Directory=$(dirname $Path)  # /etc
Filename=$(basename $Path)  # resolv.conf

# Get filename parts
Name="${Filename%.*}"  # resolv
Ext="${Filename##*.}"  # conf
Skip the first N lines from an output
## -n +N outputs starting on line N.
## Eg. To skip the first 2 lines, we start on line 3:
# tail -n+3
Get the current Unix timestamp
$ date +"%s"
1596137957
Unix timestamp to human readable time.

You can use either awk with strftime, or with date.

$ echo 1355514777 | awk '{ print strftime("%c", $1) }'
Fri 14 Dec 2012 12:52:57 PM MST
$ date +"%c" -d @15996137957
Thu 30 Jul 2020 01:39:17 PM MDT
Get a file's last modified time in Unix timestamp stat -c %Y filename
Convert a multi-line list into a comma delimited string cat list | paste -s -d, -
Get all IPv4 addresses on this host ip a | grep -ohE 'inet [0-9.]{7,15}'
Get the URL from <a href="..."> echo '<a href="asdf">' | grep -Eo 'href="[^\"]+"' | sed 's/href="\(.*\)"/\1/'
Replace substring in Bash
Use ${variable/search_term/replace_term}. Eg:
Path="/etc/resolv.conf"

# This returns /etc/hostname
echo ${Path/resolv.conf/hostname}

Useful scripting patterns

Get a specific column

You can use either awk with delimiters specified with -F.

$ echo one two three four five | awk '{print $3}'
three

$ echo one,two,three,four,five | awk -F, '{print $3}'
three

Or cut by a specific delimiter and select a specific column. This method cannot handle columns separated by more than one space. If you have neat columns, you'll need to pipe through tr -s ' ' first which collapses (or squeezes) the given repeating characters down.

$ echo one two three four five | cut -d ' ' -f3
three
$ echo "one    two    three    four     five" | tr -s ' ' | cut -d ' ' -f3
three

$ echo one,two,three,four,five | cut -d ',' -f3
three

Or use the bash built-in function read which can take in a list of variables. Variable assignment by piping to read works in zsh but will only work if used within a subshell in bash. Keep this behavior in mind if you choose to use this method of separating out words in a string. For example:

## Basic variable assignment with read
$ echo one two three four five | while read _ second _; do echo $second ; done
two

## zsh variable assignment works without a subshell/loop
% echo one two three four five | read _ second _
% echo $second 
two

The last variable contains all remaining columns. Unwanted columns can be named repeatedly with _ (like in golang) and ignored.

## Remember, to get only the 3rd column, we need to read the 4th column out.
% echo one two three four five | read _ _ third; echo $third
three four five
% echo one two three four five | read _ _ third _ ; echo $third
three

## Delimiters specified with IFS
% echo one,two,three,four,five | IFS=',' read _ _ third _ ; echo $third
three

In terms of speed, be aware that using awk or tr+cut are actual commands that spawns a sub-process whereas read is a bash built-in function. As a result, it's always faster to use read to parse out data, especially if in a loop. If you are looping and using awk or cut, perhaps consider replacing it with the bash read equivalent. For example:

## Super slow! Invokes awk 2 times per line
$ w | tail -n +3 | while read i ; do
  Username=$(echo "$i" | awk '{print $1}')
  Source=$(echo "$i" | awk '{print $3}')
  echo $Username $Source
done

## Instead do something like this!
$ w | tail -n +3 | while read Username _ Source _ ; do
  echo $Username $Source
done

Preserve header while sorting without using a temporary file

You can sort an output on a specific column while preserving the header and without needing a temporary file by using a shim function that reads and prints the first line before passing the rest of the output down the pipe. Define this function:

# print the header (the first line of input)
# and then run the specified command on the body (the rest of the input)
# use it in a pipeline, e.g. ps | body grep somepattern
body() {
    IFS= read -r header
    printf '%s\n' "$header"
    "$@"
}

Then use this shim body function with the desired sort function. For example:

$ top -b -n 1 | head -n 15 | tail -n +7 \
  | body sort -k 5 -rn

Credit to https://unix.stackexchange.com/questions/11856/sort-but-keep-header-line-at-the-top for this neat trick.

Testing things in Bash

test is a bash built-in function to add conditionals in your script. [ is an alias of the test command and are analogous.

Use the test operators listed below with test or a single bracket test condition like the following example.

$ test -e /etc/hosts && echo "File exists"
File exists

## Note that the white space around [ and ] and also the operator are required.
$ [ -e /etc/hosts ] && echo "File exists"
File exists

$ if [ -e /etc/hosts ] ; then echo "File exists" ; fi
File exists
Operator Description
-e file exists
-a file exists (deprecated)

This is identical in effect to -e

-f file is a regular file (not a directory or device file)
-s file is not zero size
-d file is a directory
-b file is a block device. Eg. /dev/sda.
-c file is a character device. Eg. /dev/tty0.
-p file is a pipe. Eg. /dev/fd/0.
-h file is a symbolic link
-L file is a symbolic link
-S file is a socket
-t file (descriptor) is associated with a terminal device

This test option may be used to check whether the stdin [ -t 0 ] or stdout [ -t 1 ] in a given script is a terminal.

-r file has read permission (for the user running the test)
-w file has write permission (for the user running the test)
-x file has execute permission (for the user running the test)
-g file or directory has set-group-id (sgid) flag set

If a directory has the sgid flag set, then a file created within that directory belongs to the group that owns the directory.

-u file has set-user-id (suid) flag set
-k file or directory has sticky bit set
  • If set on a file, that file will be kept in cache memory, for quicker access.
  • If set on a directory, it restricts write permission. Setting the sticky bit adds a t to the permissions on the file or directory listing. This restricts altering or deleting specific files in that directory to the owner of those files.
-O you are owner of file
-G group-id of file same as yours
-N file modified since it was last read
f1 -nt f2 file f1 is newer than f2
f1 -ot f2 file f1 is older than f2
f1 -ef f2 files f1 and f2 are hard links to the same file
! "not" -- reverses the sense of the tests above (returns true if condition absent).

Integers

Integer tests, with test or inside single brackets.

Eg.

$ test 2 -eq 2 && echo equal


Operator Description
-eq is equal to
-ne is not equal to
-gt is greater than
-ge is greater than or equal to
-lt is less than
-le is less than or equal to


Integer tests can also be done inside double parentheses. Eg.

$ ((2==2)) && echo equal


Operator Description
== is equal
< is less than
<= is less than or equal to
> is greater than
>= is greater than or equal to


Strings

Operator Description
-z string is null, that is, has zero length
-n string is not null.
= is equal to
## Does not work in zsh. Works only in sh and bash.
$ if [ "$a" = "$b" ] ; then
  echo "Equal"
fi
== is equal to
## Does not work in zsh. Works only in sh and bash.
$ if [ "$a" == "$b" ] ; then
  echo "Equal"
fi

This operator inside a double bracket [[ ]] construct does pattern matching.

!= is not equal to
$ if [ "$a" != "$b" ] ; then
  echo "Not equal"
fi

This operator inside a double bracket [[ ]] construct does pattern matching.

=~ Match using regex
$ if [ zone36-ta =~ .*ta$ ] ; then
  echo "matches"
fi

$ if [ zone36-ta =~ *ta ] ; then
  echo "matches"
fi


Command Substitution

Command substitution allows output from a command to be substituted in place of the command itself in a script. Commands can be enclosed within backticks `command` or the POSIX compatible form $(command).

## Eg.
echo "Today is " $(date)
echo "Today is " `date`

Do not confuse $(command) with $((expression)) which is used for arithmetic expansion.

Nested Substitution

If you are trying to do something similar to:

Something=`basename `ls /home/*.txt``

You will get an error since the expansion is ambiguous. Instead, use the $(command) substitution instead:

Something=$(basename $(ls /home/*.txt))


Arithmetic Operations

Do integer arithmetic operations on variables within double brackets. For example:

Value=1
ValuePlusOne=$((Value + 1))
ValueTimes12=$((Value * 12))

The operators that are supported are:

+ - / * % 

Bitwise operators also work:

^ (XOR)   | (OR)   & (AND)   << (Shift left)   >> (Shift right)

Control Blocks

Switch

read -p "What do you want to do?: " Option

case "$Option" in
	"K")
		install_new_kernel
		die "Complete!"
		;;
	"H")
		usage
		;;
	*)
		die "Stopping installation."
		;;
esac

See Also