Bash Scripting: Difference between revisions
m Text replacement - "</code>" to "}}" |
mNo edit summary |
||
| (39 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
== | ==Useful functions== | ||
=== | 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. | |||
This is useful if your script uses relative paths. | |||
|{{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. | |||
'''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"> | |||
function die { | |||
echo $@ | |||
exit | |||
} | |||
</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 | |||
## zsh variable assignment works without a subshell/loop | |||
% echo one two three four five {{!}} read _ second _ | |||
% echo $second | |||
two | |||
| lang = terminal | |||
}}The last variable contains all remaining columns. Unwanted columns can be named repeatedly with <code>_</code> (like in golang) and ignored.{{Highlight | |||
| code = ## 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 | |||
| 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 | |||
## Instead do something like this! | |||
$ w {{!}} tail -n +3 {{!}} while read Username _ Source _ ; do | |||
echo $Username $Source | |||
done | |||
| lang = terminal | |||
}} | |||
===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: | |||
{{Highlight | |||
| code = # 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" | |||
"$@" | |||
} | |||
| 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= | {{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= | {{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= | {{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=== | |||
== | {| class="wikitable" | ||
! 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 | |||
{{highlight|lang=terminal|code= | |||
## Does not work in zsh. Works only in sh and bash. | |||
$ if [ "$a" = "$b" ] ; then | |||
echo "Equal" | |||
fi | |||
}} | |||
|- | |||
!{{code|1===}} | |||
|is equal to | |||
{{highlight|lang=terminal|code= | {{highlight|lang=terminal|code= | ||
## Does not work in zsh. Works only in sh and bash. | |||
$ if [ "$a" == "$b" ] ; then | |||
echo " | echo "Equal" | ||
fi | |||
}} | }} | ||
This operator inside a double bracket {{code|[[ ]]}} construct does pattern matching. | |||
|- | |||
!{{code|1=!=}} | |||
|is not equal to | |||
{{highlight|lang=terminal|code= | {{highlight|lang=terminal|code= | ||
$ | $ if [ "$a" != "$b" ] ; then | ||
echo "Not equal" | |||
fi | |||
}} | }} | ||
This operator inside a double bracket {{code|[[ ]]}} construct does pattern matching. | |||
|- | |||
!{{code|1==~}} | |||
|Match using regex | |||
{{highlight|lang=terminal|code= | {{highlight|lang=terminal|code= | ||
$ | $ if [ zone36-ta =~ .*ta$ ] ; then | ||
echo "matches" | |||
fi | |||
$ if [ zone36-ta =~ *ta ] ; then | |||
echo "matches" | |||
fi | |||
}} | }} | ||
|}<br /> | |||
==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 {{code|`command`}} or the POSIX compatible form {{code|$(command)}}. | |||
{{highlight|lang=terminal|code= | {{highlight|lang=terminal|code= | ||
## Eg. | |||
echo "Today is " $(date) | |||
echo "Today is " `date` | |||
}} | }} | ||
== Arithmetic Operations == | Do not confuse {{code|$(command)}} with {{code|$((expression))}} which is used for arithmetic expansion. | ||
===Nested Substitution=== | |||
If you are trying to do something similar to: | |||
{{highlight|lang=bash|code= | |||
Something=`basename `ls /home/*.txt`` | |||
}} | |||
You will get an error since the expansion is ambiguous. Instead, use the {{code|$(command)}} substitution instead: | |||
{{highlight|lang=bash|code= | |||
Something=$(basename $(ls /home/*.txt)) | |||
}} | |||
==Arithmetic Operations== | |||
Do integer arithmetic operations on variables within double brackets. For example: | Do integer arithmetic operations on variables within double brackets. For example: | ||
{{highlight|lang=terminal|code= | {{highlight|lang=terminal|code= | ||
| Line 91: | Line 455: | ||
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= | {{highlight|lang=terminal|code= | ||
read -p "What do you want to do?: " Option | read -p "What do you want to do?: " Option | ||
| Line 112: | Line 476: | ||
}} | }} | ||
== | ==See Also== | ||
*https://www.tldp.org/LDP/abs/html/comparison-ops.html | |||
*http://sipb.mit.edu/doc/safe-shell/ | |||
*http://mywiki.wooledge.org/BashFAQ | |||
*https://github.com/dylanaraps/pure-sh-bible | |||
{{Navbox Linux}} | |||
[[Category:Linux]] | [[Category: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 |
$ 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 |
|
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 | |
-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
| |
-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 |
!=
|
is not equal to
$ if [ "$a" != "$b" ] ; then
echo "Not equal"
fi
This operator inside a double bracket |
=~
|
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
- https://www.tldp.org/LDP/abs/html/comparison-ops.html
- http://sipb.mit.edu/doc/safe-shell/
- http://mywiki.wooledge.org/BashFAQ
- https://github.com/dylanaraps/pure-sh-bible