diff --git a/manuscript/Book.txt b/manuscript/Book.txt index b4a1249..b56dfe1 100644 --- a/manuscript/Book.txt +++ b/manuscript/Book.txt @@ -17,3 +17,4 @@ chapter15.txt chapter16.txt chapter17.txt chapter18.txt +chapter19.txt diff --git a/manuscript/chapter10.txt b/manuscript/chapter10.txt index 36c5887..d7d7ac4 100644 --- a/manuscript/chapter10.txt +++ b/manuscript/chapter10.txt @@ -1,58 +1,59 @@ -# ARITHMETIC OPERATORS +# CONDITIONAL EXPRESSIONS -## Assignment +## File Conditionals -| Operators | What does it do? | -| --------- | ---------------- | -| `=` | Initialize or change the value of a variable. +| Expression | Value | What does it do? | +| ---------- | ------ | ---------------- | +| `-a` | `file` | If file exists. +| `-b` | `file` | If file exists and is a block special file. +| `-c` | `file` | If file exists and is a character special file. +| `-d` | `file` | If file exists and is a directory. +| `-e` | `file` | If file exists. +| `-f` | `file` | If file exists and is a regular file. +| `-g` | `file` | If file exists and its set-group-id bit is set. +| `-h` | `file` | If file exists and is a symbolic link. +| `-k` | `file` | If file exists and its sticky-bit is set +| `-p` | `file` | If file exists and is a named pipe (*FIFO*). +| `-r` | `file` | If file exists and is readable. +| `-s` | `file` | If file exists and its size is greater than zero. +| `-t` | `fd` | If file descriptor is open and refers to a terminal. +| `-u` | `file` | If file exists and its set-user-id bit is set. +| `-w` | `file` | If file exists and is writable. +| `-x` | `file` | If file exists and is executable. +| `-G` | `file` | If file exists and is owned by the effective group ID. +| `-L` | `file` | If file exists and is a symbolic link. +| `-N` | `file` | If file exists and has been modified since last read. +| `-O` | `file` | If file exists and is owned by the effective user ID. +| `-S` | `file` | If file exists and is a socket. -## Arithmetic +## File Comparisons -| Operators | What does it do? | -| --------- | ---------------- | -| `+` | Addition -| `-` | Subtraction -| `*` | Multiplication -| `/` | Division -| `**` | Exponentiation -| `%` | Modulo -| `+=` | Plus-Equal (*Increment a variable.*) -| `-=` | Minus-Equal (*Decrement a variable.*) -| `*=` | Times-Equal (*Multiply a variable.*) -| `/=` | Slash-Equal (*Divide a variable.*) -| `%=` | Mod-Equal (*Remainder of dividing a variable.*) +| Expression | What does it do? | +| ---------- | ---------------- | +| `file -ef file2` | If both files refer to the same inode and device numbers. +| `file -nt file2` | If `file` is newer than `file2` (*uses modification ime*) or `file` exists and `file2` does not. +| `file -ot file2` | If `file` is older than `file2` (*uses modification ime*) or `file2` exists and `file` does not. -## Bitwise +## Variable Conditionals -| Operators | What does it do? | -| --------- | ---------------- | -| `<<` | Bitwise Left Shift -| `<<=` | Left-Shift-Equal -| `>>` | Bitwise Right Shift -| `>>=` | Right-Shift-Equal -| `&` | Bitwise AND -| `&=` | Bitwise AND-Equal -| `\|` | Bitwise OR -| `\|=` | Bitwise OR-Equal -| `~` | Bitwise NOT -| `^` | Bitwise XOR -| `^=` | Bitwise XOR-Equal +| Expression | Value | What does it do? | +| ---------- | ----- | ---------------- | +| `-o` | `opt` | If shell option is enabled. +| `-v` | `var` | If variable has a value assigned. +| `-R` | `var` | If variable is a name reference. +| `-z` | `var` | If the length of string is zero. +| `-n` | `var` | If the length of string is non-zero. -## Logical - -| Operators | What does it do? | -| --------- | ---------------- | -| `!` | NOT -| `&&` | AND -| `\|\|` | OR - -## Miscellaneous - -| Operators | What does it do? | Example | -| --------- | ---------------- | ------- | -| `,` | Comma Separator | `((a=1,b=2,c=3))` +## Variable Comparisons +| Expression | What does it do? | +| ---------- | ---------------- | +| `var = var2` | Equal to. +| `var == var2` | Equal to (*synonym for `=`*). +| `var != var2` | Not equal to. +| `var < var2` | Less than (*in ASCII alphabetical order.*) +| `var > var2` | Greater than (*in ASCII alphabetical order.*) diff --git a/manuscript/chapter11.txt b/manuscript/chapter11.txt index e7e3f0b..fedbce7 100644 --- a/manuscript/chapter11.txt +++ b/manuscript/chapter11.txt @@ -1,31 +1,58 @@ -# ARITHMETIC -## Simpler syntax to set variables +# ARITHMETIC OPERATORS -```shell -# Simple math -((var=1+2)) +## Assignment -# Decrement/Increment variable -((var++)) -((var--)) -((var+=1)) -((var-=1)) +| Operators | What does it do? | +| --------- | ---------------- | +| `=` | Initialize or change the value of a variable. -# Using variables -((var=var2*arr[2])) -``` +## Arithmetic -## Ternary Tests +| Operators | What does it do? | +| --------- | ---------------- | +| `+` | Addition +| `-` | Subtraction +| `*` | Multiplication +| `/` | Division +| `**` | Exponentiation +| `%` | Modulo +| `+=` | Plus-Equal (*Increment a variable.*) +| `-=` | Minus-Equal (*Decrement a variable.*) +| `*=` | Times-Equal (*Multiply a variable.*) +| `/=` | Slash-Equal (*Divide a variable.*) +| `%=` | Mod-Equal (*Remainder of dividing a variable.*) + +## Bitwise + +| Operators | What does it do? | +| --------- | ---------------- | +| `<<` | Bitwise Left Shift +| `<<=` | Left-Shift-Equal +| `>>` | Bitwise Right Shift +| `>>=` | Right-Shift-Equal +| `&` | Bitwise AND +| `&=` | Bitwise AND-Equal +| `\|` | Bitwise OR +| `\|=` | Bitwise OR-Equal +| `~` | Bitwise NOT +| `^` | Bitwise XOR +| `^=` | Bitwise XOR-Equal + +## Logical + +| Operators | What does it do? | +| --------- | ---------------- | +| `!` | NOT +| `&&` | AND +| `\|\|` | OR + +## Miscellaneous + +| Operators | What does it do? | Example | +| --------- | ---------------- | ------- | +| `,` | Comma Separator | `((a=1,b=2,c=3))` -```shell -# Set the value of var to var2 if var2 is greater than var. -# var: variable to set. -# var2>var: Condition to test. -# ?var2: If the test succeeds. -# :var: If the test fails. -((var=var2>var?var2:var)) -``` diff --git a/manuscript/chapter12.txt b/manuscript/chapter12.txt index 14c380b..e7e3f0b 100644 --- a/manuscript/chapter12.txt +++ b/manuscript/chapter12.txt @@ -1,42 +1,30 @@ -# TRAPS +# ARITHMETIC -Traps allow a script to execute code on various signals. In [pxltrm](https://github.com/dylanaraps/pxltrm) (*a pixel art editor written in bash*) traps are used to redraw the user interface on window resize. Another use case is cleaning up temporary files on script exit. - -Traps should be added near the start of scripts so any early errors are also caught. - -**NOTE:** For a full list of signals, see `trap -l`. - - -## Do something on script exit +## Simpler syntax to set variables ```shell -# Clear screen on script exit. -trap 'printf \\e[2J\\e[H\\e[m' EXIT +# Simple math +((var=1+2)) + +# Decrement/Increment variable +((var++)) +((var--)) +((var+=1)) +((var-=1)) + +# Using variables +((var=var2*arr[2])) ``` -## Ignore terminal interrupt (CTRL+C, SIGINT) +## Ternary Tests ```shell -trap '' INT -``` - -## React to window resize - -```shell -# Call a function on window resize. -trap 'code_here' SIGWINCH -``` - -## Do something before every command - -```shell -trap 'code_here' DEBUG -``` - -## Do something when a shell function or a sourced file finishes executing - -```shell -trap 'code_here' RETURN +# Set the value of var to var2 if var2 is greater than var. +# var: variable to set. +# var2>var: Condition to test. +# ?var2: If the test succeeds. +# :var: If the test fails. +((var=var2>var?var2:var)) ``` diff --git a/manuscript/chapter13.txt b/manuscript/chapter13.txt index 3e49886..14c380b 100644 --- a/manuscript/chapter13.txt +++ b/manuscript/chapter13.txt @@ -1,13 +1,42 @@ -# PERFORMANCE +# TRAPS -## Disable Unicode +Traps allow a script to execute code on various signals. In [pxltrm](https://github.com/dylanaraps/pxltrm) (*a pixel art editor written in bash*) traps are used to redraw the user interface on window resize. Another use case is cleaning up temporary files on script exit. -If unicode is not required, it can be disabled for a performance increase. Results may vary however there have been noticeable improvements in [neofetch](https://github.com/dylanaraps/neofetch) and other programs. +Traps should be added near the start of scripts so any early errors are also caught. + +**NOTE:** For a full list of signals, see `trap -l`. + + +## Do something on script exit ```shell -# Disable unicode. -LC_ALL=C -LANG=C +# Clear screen on script exit. +trap 'printf \\e[2J\\e[H\\e[m' EXIT +``` + +## Ignore terminal interrupt (CTRL+C, SIGINT) + +```shell +trap '' INT +``` + +## React to window resize + +```shell +# Call a function on window resize. +trap 'code_here' SIGWINCH +``` + +## Do something before every command + +```shell +trap 'code_here' DEBUG +``` + +## Do something when a shell function or a sourced file finishes executing + +```shell +trap 'code_here' RETURN ``` diff --git a/manuscript/chapter14.txt b/manuscript/chapter14.txt index e03af79..3e49886 100644 --- a/manuscript/chapter14.txt +++ b/manuscript/chapter14.txt @@ -1,51 +1,13 @@ -# OBSOLETE SYNTAX +# PERFORMANCE -## Shebang +## Disable Unicode -Use `#!/usr/bin/env bash` instead of `#!/bin/bash`. - -- The former searches the user's `PATH` to find the `bash` binary. -- The latter assumes it is always installed to `/bin/` which can cause issues. +If unicode is not required, it can be disabled for a performance increase. Results may vary however there have been noticeable improvements in [neofetch](https://github.com/dylanaraps/neofetch) and other programs. ```shell -# Right: - - #!/usr/bin/env bash - -# Wrong: - - #!/bin/bash -``` - -## Command Substitution - -Use `$()` instead of `` ` ` ``. - -```shell -# Right. -var="$(command)" - -# Wrong. -var=`command` - -# $() can easily be nested whereas `` cannot. -var="$(command "$(command)")" -``` - -## Function Declaration - -Do not use the `function` keyword, it reduces compatibility with older versions of `bash`. - -```shell -# Right. -do_something() { - # ... -} - -# Wrong. -function do_something() { - # ... -} +# Disable unicode. +LC_ALL=C +LANG=C ``` diff --git a/manuscript/chapter15.txt b/manuscript/chapter15.txt index 2aaaa32..e03af79 100644 --- a/manuscript/chapter15.txt +++ b/manuscript/chapter15.txt @@ -1,93 +1,51 @@ -# INTERNAL VARIABLES +# OBSOLETE SYNTAX -## Get the location to the `bash` binary +## Shebang + +Use `#!/usr/bin/env bash` instead of `#!/bin/bash`. + +- The former searches the user's `PATH` to find the `bash` binary. +- The latter assumes it is always installed to `/bin/` which can cause issues. ```shell -"$BASH" +# Right: + + #!/usr/bin/env bash + +# Wrong: + + #!/bin/bash ``` -## Get the version of the current running `bash` process +## Command Substitution + +Use `$()` instead of `` ` ` ``. ```shell -# As a string. -"$BASH_VERSION" +# Right. +var="$(command)" -# As an array. -"${BASH_VERSINFO[@]}" +# Wrong. +var=`command` + +# $() can easily be nested whereas `` cannot. +var="$(command "$(command)")" ``` -## Open the user's preferred text editor +## Function Declaration + +Do not use the `function` keyword, it reduces compatibility with older versions of `bash`. ```shell -"$EDITOR" "$file" +# Right. +do_something() { + # ... +} -# NOTE: This variable may be empty, set a fallback value. -"${EDITOR:-vi}" "$file" -``` - -## Get the name of the current function - -```shell -# Current function. -"${FUNCNAME[0]}" - -# Parent function. -"${FUNCNAME[1]}" - -# So on and so forth. -"${FUNCNAME[2]}" -"${FUNCNAME[3]}" - -# All functions including parents. -"${FUNCNAME[@]}" -``` - -## Get the host-name of the system - -```shell -"$HOSTNAME" - -# NOTE: This variable may be empty. -# Optionally set a fallback to the hostname command. -"${HOSTNAME:-$(hostname)}" -``` - -## Get the architecture of the Operating System - -```shell -"$HOSTTYPE" -``` - -## Get the name of the Operating System / Kernel - -This can be used to add conditional support for different Operating -Systems without needing to call `uname`. - -```shell -"$OSTYPE" -``` - -## Get the current working directory - -This is an alternative to the `pwd` built-in. - -```shell -"$PWD" -``` - -## Get the number of seconds the script has been running - -```shell -"$SECONDS" -``` - -## Get a pseudorandom integer - -Each time `$RANDOM` is used, a different integer between `0` and `32767` is returned. This variable should not be used for anything related to security (*this includes encryption keys etc*). - - -```shell -"$RANDOM" +# Wrong. +function do_something() { + # ... +} ``` diff --git a/manuscript/chapter16.txt b/manuscript/chapter16.txt index 9ebc228..2aaaa32 100644 --- a/manuscript/chapter16.txt +++ b/manuscript/chapter16.txt @@ -1,78 +1,93 @@ -# INFORMATION ABOUT THE TERMINAL +# INTERNAL VARIABLES -## Get the terminal size in lines and columns (*from a script*) - -This is handy when writing scripts in pure bash and `stty`/`tput` can’t be -called. - -**Example Function:** - -```sh -get_term_size() { - # Usage: get_term_size - - # (:;:) is a micro sleep to ensure the variables are - # exported immediately. - shopt -s checkwinsize; (:;:) - printf '%s\n' "$LINES $COLUMNS" -} -``` - -**Example Usage:** +## Get the location to the `bash` binary ```shell -# Output: LINES COLUMNS -$ get_term_size -15 55 +"$BASH" ``` -## Get the terminal size in pixels - -**CAVEAT**: This does not work in some terminal emulators. - -**Example Function:** - -```sh -get_window_size() { - # Usage: get_window_size - printf '%b' "${TMUX:+\\ePtmux;\\e}\\e[14t${TMUX:+\\e\\\\}" - IFS=';t' read -d t -t 0.05 -sra term_size - printf '%s\n' "${term_size[1]}x${term_size[2]}" -} -``` - -**Example Usage:** +## Get the version of the current running `bash` process ```shell -# Output: WIDTHxHEIGHT -$ get_window_size -1200x800 +# As a string. +"$BASH_VERSION" -# Output (fail): -$ get_window_size -x +# As an array. +"${BASH_VERSINFO[@]}" ``` -## Get the current cursor position - -This is useful when creating a TUI in pure bash. - -**Example Function:** - -```sh -get_cursor_pos() { - # Usage: get_cursor_pos - IFS='[;' read -p $'\e[6n' -d R -rs _ y x _ - printf '%s\n' "$x $y" -} -``` - -**Example Usage:** +## Open the user's preferred text editor ```shell -# Output: X Y -$ get_cursor_pos -1 8 +"$EDITOR" "$file" + +# NOTE: This variable may be empty, set a fallback value. +"${EDITOR:-vi}" "$file" +``` + +## Get the name of the current function + +```shell +# Current function. +"${FUNCNAME[0]}" + +# Parent function. +"${FUNCNAME[1]}" + +# So on and so forth. +"${FUNCNAME[2]}" +"${FUNCNAME[3]}" + +# All functions including parents. +"${FUNCNAME[@]}" +``` + +## Get the host-name of the system + +```shell +"$HOSTNAME" + +# NOTE: This variable may be empty. +# Optionally set a fallback to the hostname command. +"${HOSTNAME:-$(hostname)}" +``` + +## Get the architecture of the Operating System + +```shell +"$HOSTTYPE" +``` + +## Get the name of the Operating System / Kernel + +This can be used to add conditional support for different Operating +Systems without needing to call `uname`. + +```shell +"$OSTYPE" +``` + +## Get the current working directory + +This is an alternative to the `pwd` built-in. + +```shell +"$PWD" +``` + +## Get the number of seconds the script has been running + +```shell +"$SECONDS" +``` + +## Get a pseudorandom integer + +Each time `$RANDOM` is used, a different integer between `0` and `32767` is returned. This variable should not be used for anything related to security (*this includes encryption keys etc*). + + +```shell +"$RANDOM" ``` diff --git a/manuscript/chapter17.txt b/manuscript/chapter17.txt index 0da32b3..9ebc228 100644 --- a/manuscript/chapter17.txt +++ b/manuscript/chapter17.txt @@ -1,145 +1,78 @@ -# CONVERSION +# INFORMATION ABOUT THE TERMINAL -## Convert a hex color to RGB +## Get the terminal size in lines and columns (*from a script*) + +This is handy when writing scripts in pure bash and `stty`/`tput` can’t be +called. **Example Function:** ```sh -hex_to_rgb() { - # Usage: hex_to_rgb "#FFFFFF" - : "${1/\#}" - ((r=16#${_:0:2},g=16#${_:2:2},b=16#${_:4:2})) - printf '%s\n' "$r $g $b" +get_term_size() { + # Usage: get_term_size + + # (:;:) is a micro sleep to ensure the variables are + # exported immediately. + shopt -s checkwinsize; (:;:) + printf '%s\n' "$LINES $COLUMNS" } ``` **Example Usage:** ```shell -$ hex_to_rgb "#FFFFFF" -255 255 255 +# Output: LINES COLUMNS +$ get_term_size +15 55 ``` +## Get the terminal size in pixels -## Convert an RGB color to hex +**CAVEAT**: This does not work in some terminal emulators. **Example Function:** ```sh -rgb_to_hex() { - # Usage: rgb_to_hex "r" "g" "b" - printf '#%02x%02x%02x\n' "$1" "$2" "$3" +get_window_size() { + # Usage: get_window_size + printf '%b' "${TMUX:+\\ePtmux;\\e}\\e[14t${TMUX:+\\e\\\\}" + IFS=';t' read -d t -t 0.05 -sra term_size + printf '%s\n' "${term_size[1]}x${term_size[2]}" } ``` **Example Usage:** ```shell -$ rgb_to_hex "255" "255" "255" -#FFFFFF +# Output: WIDTHxHEIGHT +$ get_window_size +1200x800 + +# Output (fail): +$ get_window_size +x ``` +## Get the current cursor position -# CODE GOLF +This is useful when creating a TUI in pure bash. -## Shorter `for` loop syntax +**Example Function:** -```shell -# Tiny C Style. -for((;i++<10;)){ echo "$i";} - -# Undocumented method. -for i in {1..10};{ echo "$i";} - -# Expansion. -for i in {1..10}; do echo "$i"; done - -# C Style. -for((i=0;i<=10;i++)); do echo "$i"; done -``` - -## Shorter infinite loops - -```shell -# Normal method -while :; do echo hi; done - -# Shorter -for((;;)){ echo hi;} -``` - -## Shorter function declaration - -```shell -# Normal method -f(){ echo hi;} - -# Using a subshell -f()(echo hi) - -# Using arithmetic -# This can be used to assign integer values. -# Example: f a=1 -# f a++ -f()(($1)) - -# Using tests, loops etc. -# NOTE: ‘while’, ‘until’, ‘case’, ‘(())’, ‘[[]]’ can also be used. -f()if true; then echo "$1"; fi -f()for i in "$@"; do echo "$i"; done -``` - -## Shorter `if` syntax - -```shell -# One line -# Note: The 3rd statement may run when the 1st is true -[[ "$var" == hello ]] && echo hi || echo bye -[[ "$var" == hello ]] && { echo hi; echo there; } || echo bye - -# Multi line (no else, single statement) -# Note: The exit status may not be the same as with an if statement -[[ "$var" == hello ]] && \ - echo hi - -# Multi line (no else) -[[ "$var" == hello ]] && { - echo hi - # ... +```sh +get_cursor_pos() { + # Usage: get_cursor_pos + IFS='[;' read -p $'\e[6n' -d R -rs _ y x _ + printf '%s\n' "$x $y" } ``` -## Simpler `case` statement to set variable - -The `:` built-in can be used to avoid repeating `variable=` in a case statement. The `$_` variable stores the last argument of the last command. `:` always succeeds so it can be used to store the variable value. +**Example Usage:** ```shell -# Modified snippet from Neofetch. -case "$OSTYPE" in - "darwin"*) - : "MacOS" - ;; - - "linux"*) - : "Linux" - ;; - - *"bsd"* | "dragonfly" | "bitrig") - : "BSD" - ;; - - "cygwin" | "msys" | "win32") - : "Windows" - ;; - - *) - printf '%s\n' "Unknown OS detected, aborting..." >&2 - exit 1 - ;; -esac - -# Finally, set the variable. -os="$_" +# Output: X Y +$ get_cursor_pos +1 8 ``` diff --git a/manuscript/chapter18.txt b/manuscript/chapter18.txt index f575993..a683fae 100644 --- a/manuscript/chapter18.txt +++ b/manuscript/chapter18.txt @@ -1,189 +1,146 @@ -# OTHER +# CONVERSION -## Use `read` as an alternative to the `sleep` command - -Surprisingly, `sleep` is an external command and not a `bash` built-in. - -**CAVEAT:** Requires `bash` 4+ +## Convert a hex color to RGB **Example Function:** ```sh -read_sleep() { - # Usage: sleep 1 - # sleep 0.2 - read -rst "${1:-1}" -N 999 +hex_to_rgb() { + # Usage: hex_to_rgb "#FFFFFF" + # hex_to_rgb "000000" + : "${1/\#}" + ((r=16#${_:0:2},g=16#${_:2:2},b=16#${_:4:2})) + printf '%s\n' "$r $g $b" } ``` **Example Usage:** ```shell -read_sleep 1 -read_sleep 0.1 -read_sleep 30 +$ hex_to_rgb "#FFFFFF" +255 255 255 ``` -## Check if a program is in the user's PATH -```shell -# There are 3 ways to do this and either one can be used. -type -p executable_name &>/dev/null -hash executable_name &>/dev/null -command -v executable_name &>/dev/null - -# As a test. -if type -p executable_name &>/dev/null; then - # Program is in PATH. -fi - -# Inverse. -if ! type -p executable_name &>/dev/null; then - # Program is not in PATH. -fi - -# Example (Exit early if program is not installed). -if ! type -p convert &>/dev/null; then - printf '%s\n' "error: convert is not installed, exiting..." - exit 1 -fi -``` - -## Get the current date using `strftime` - -Bash’s `printf` has a built-in method of getting the date which can be used in place of the `date` command. - -**CAVEAT:** Requires `bash` 4+ +## Convert an RGB color to hex **Example Function:** ```sh -date() { - # Usage: date "format" - # See: 'man strftime' for format. - printf "%($1)T\\n" "-1" +rgb_to_hex() { + # Usage: rgb_to_hex "r" "g" "b" + printf '#%02x%02x%02x\n' "$1" "$2" "$3" } ``` **Example Usage:** ```shell -# Using above function. -$ date "%a %d %b - %l:%M %p" -Fri 15 Jun - 10:00 AM - -# Using printf directly. -$ printf '%(%a %d %b - %l:%M %p)T\n' "-1" -Fri 15 Jun - 10:00 AM - -# Assigning a variable using printf. -$ printf -v date '%(%a %d %b - %l:%M %p)T\n' '-1' -$ printf '%s\n' "$date" -Fri 15 Jun - 10:00 AM +$ rgb_to_hex "255" "255" "255" +#FFFFFF ``` -## Generate a UUID V4 -**Example Function:** +# CODE GOLF -```sh -uuid() { - # Usage: uuid - C="89ab" +## Shorter `for` loop syntax - for ((N=0;N<16;++N)); do - B="$((RANDOM%256))" +```shell +# Tiny C Style. +for((;i++<10;)){ echo "$i";} - case "$N" in - 6) printf '4%x' "$((B%16))" ;; - 8) printf '%c%x' "${C:$RANDOM%${#C}:1}" "$((B%16))" ;; +# Undocumented method. +for i in {1..10};{ echo "$i";} - 3|5|7|9) - printf '%02x-' "$B" - ;; +# Expansion. +for i in {1..10}; do echo "$i"; done - *) - printf '%02x' "$B" - ;; - esac - done +# C Style. +for((i=0;i<=10;i++)); do echo "$i"; done +``` - printf '\n' +## Shorter infinite loops + +```shell +# Normal method +while :; do echo hi; done + +# Shorter +for((;;)){ echo hi;} +``` + +## Shorter function declaration + +```shell +# Normal method +f(){ echo hi;} + +# Using a subshell +f()(echo hi) + +# Using arithmetic +# This can be used to assign integer values. +# Example: f a=1 +# f a++ +f()(($1)) + +# Using tests, loops etc. +# NOTE: ‘while’, ‘until’, ‘case’, ‘(())’, ‘[[]]’ can also be used. +f()if true; then echo "$1"; fi +f()for i in "$@"; do echo "$i"; done +``` + +## Shorter `if` syntax + +```shell +# One line +# Note: The 3rd statement may run when the 1st is true +[[ "$var" == hello ]] && echo hi || echo bye +[[ "$var" == hello ]] && { echo hi; echo there; } || echo bye + +# Multi line (no else, single statement) +# Note: The exit status may not be the same as with an if statement +[[ "$var" == hello ]] && \ + echo hi + +# Multi line (no else) +[[ "$var" == hello ]] && { + echo hi + # ... } ``` -**Example Usage:** +## Simpler `case` statement to set variable + +The `:` built-in can be used to avoid repeating `variable=` in a case statement. The `$_` variable stores the last argument of the last command. `:` always succeeds so it can be used to store the variable value. ```shell -$ uuid -d5b6c731-1310-4c24-9fe3-55d556d44374 -``` +# Modified snippet from Neofetch. +case "$OSTYPE" in + "darwin"*) + : "MacOS" + ;; -## Progress bars + "linux"*) + : "Linux" + ;; -This is a simple way of drawing progress bars without needing a for loop -in the function itself. + *"bsd"* | "dragonfly" | "bitrig") + : "BSD" + ;; -**Example Function:** + "cygwin" | "msys" | "win32") + : "Windows" + ;; -```sh -bar() { - # Usage: bar 1 10 - # ^----- Elapsed Percentage (0-100). - # ^-- Total length in chars. - ((elapsed=$1*$2/100)) + *) + printf '%s\n' "Unknown OS detected, aborting..." >&2 + exit 1 + ;; +esac - # Create the bar with spaces. - printf -v prog "%${elapsed}s" - printf -v total "%$(($2-elapsed))s" - - printf '%s\r' "[${prog// /-}${total}]" -} -``` - -**Example Usage:** - -```shell -for ((i=0;i<=100;i++)); do - # Pure bash micro sleeps (for the example). - (:;:) && (:;:) && (:;:) && (:;:) && (:;:) - - # Print the bar. - bar "$i" "10" -done - -printf '\n' -``` - -## Get the list of functions in a script - -```sh -get_functions() { - # Usage: get_functions - IFS=$'\n' read -d "" -ra functions < <(declare -F) - printf '%s\n' "${functions[@]//declare -f }" -} -``` - -## Bypass shell aliases - -```shell -# alias -ls - -# command -# shellcheck disable=SC1001 -\ls -``` - -## Bypass shell functions - -```shell -# function -ls - -# command -command ls +# Finally, set the variable. +os="$_" ``` diff --git a/manuscript/chapter19.txt b/manuscript/chapter19.txt new file mode 100644 index 0000000..f575993 --- /dev/null +++ b/manuscript/chapter19.txt @@ -0,0 +1,190 @@ +# OTHER + +## Use `read` as an alternative to the `sleep` command + +Surprisingly, `sleep` is an external command and not a `bash` built-in. + +**CAVEAT:** Requires `bash` 4+ + +**Example Function:** + +```sh +read_sleep() { + # Usage: sleep 1 + # sleep 0.2 + read -rst "${1:-1}" -N 999 +} +``` + +**Example Usage:** + +```shell +read_sleep 1 +read_sleep 0.1 +read_sleep 30 +``` + +## Check if a program is in the user's PATH + +```shell +# There are 3 ways to do this and either one can be used. +type -p executable_name &>/dev/null +hash executable_name &>/dev/null +command -v executable_name &>/dev/null + +# As a test. +if type -p executable_name &>/dev/null; then + # Program is in PATH. +fi + +# Inverse. +if ! type -p executable_name &>/dev/null; then + # Program is not in PATH. +fi + +# Example (Exit early if program is not installed). +if ! type -p convert &>/dev/null; then + printf '%s\n' "error: convert is not installed, exiting..." + exit 1 +fi +``` + +## Get the current date using `strftime` + +Bash’s `printf` has a built-in method of getting the date which can be used in place of the `date` command. + +**CAVEAT:** Requires `bash` 4+ + +**Example Function:** + +```sh +date() { + # Usage: date "format" + # See: 'man strftime' for format. + printf "%($1)T\\n" "-1" +} +``` + +**Example Usage:** + +```shell +# Using above function. +$ date "%a %d %b - %l:%M %p" +Fri 15 Jun - 10:00 AM + +# Using printf directly. +$ printf '%(%a %d %b - %l:%M %p)T\n' "-1" +Fri 15 Jun - 10:00 AM + +# Assigning a variable using printf. +$ printf -v date '%(%a %d %b - %l:%M %p)T\n' '-1' +$ printf '%s\n' "$date" +Fri 15 Jun - 10:00 AM +``` + +## Generate a UUID V4 + +**Example Function:** + +```sh +uuid() { + # Usage: uuid + C="89ab" + + for ((N=0;N<16;++N)); do + B="$((RANDOM%256))" + + case "$N" in + 6) printf '4%x' "$((B%16))" ;; + 8) printf '%c%x' "${C:$RANDOM%${#C}:1}" "$((B%16))" ;; + + 3|5|7|9) + printf '%02x-' "$B" + ;; + + *) + printf '%02x' "$B" + ;; + esac + done + + printf '\n' +} +``` + +**Example Usage:** + +```shell +$ uuid +d5b6c731-1310-4c24-9fe3-55d556d44374 +``` + +## Progress bars + +This is a simple way of drawing progress bars without needing a for loop +in the function itself. + +**Example Function:** + +```sh +bar() { + # Usage: bar 1 10 + # ^----- Elapsed Percentage (0-100). + # ^-- Total length in chars. + ((elapsed=$1*$2/100)) + + # Create the bar with spaces. + printf -v prog "%${elapsed}s" + printf -v total "%$(($2-elapsed))s" + + printf '%s\r' "[${prog// /-}${total}]" +} +``` + +**Example Usage:** + +```shell +for ((i=0;i<=100;i++)); do + # Pure bash micro sleeps (for the example). + (:;:) && (:;:) && (:;:) && (:;:) && (:;:) + + # Print the bar. + bar "$i" "10" +done + +printf '\n' +``` + +## Get the list of functions in a script + +```sh +get_functions() { + # Usage: get_functions + IFS=$'\n' read -d "" -ra functions < <(declare -F) + printf '%s\n' "${functions[@]//declare -f }" +} +``` + +## Bypass shell aliases + +```shell +# alias +ls + +# command +# shellcheck disable=SC1001 +\ls +``` + +## Bypass shell functions + +```shell +# function +ls + +# command +command ls +``` + + +