2018-06-18 02:18:18 +02:00
|
|
|
|
<p align="center"><img src="https://raw.githubusercontent.com/odb/official-bash-logo/master/assets/Logos/Icons/PNG/512x512.png" width="200px"></p>
|
2018-06-14 02:15:55 +02:00
|
|
|
|
<h1 align="center">pure bash bible</h1> <p
|
|
|
|
|
align="center">A [WIP] collection of pure bash alternatives to external
|
|
|
|
|
processes.</p>
|
2018-06-14 02:10:08 +02:00
|
|
|
|
|
2018-06-15 02:03:09 +02:00
|
|
|
|
<p align="center"> <a
|
|
|
|
|
href="https://travis-ci.com/dylanaraps/pure-bash-bible"><img
|
|
|
|
|
src="https://travis-ci.com/dylanaraps/pure-bash-bible.svg?branch=master"></a>
|
2018-06-16 15:46:18 +02:00
|
|
|
|
<a href="https://discord.gg/yfa5BDw"><img src="https://img.shields.io/discord/440354555197128704.svg"></a>
|
2018-06-15 02:03:09 +02:00
|
|
|
|
<a href="./LICENSE.md"><img
|
2018-06-16 10:44:30 +02:00
|
|
|
|
src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
|
2018-06-16 10:43:51 +02:00
|
|
|
|
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=V7QNJNKS3WYVS"><img src="https://img.shields.io/badge/donate-paypal-green.svg"></a>
|
2018-06-16 10:44:30 +02:00
|
|
|
|
</p>
|
2018-06-14 10:08:34 +02:00
|
|
|
|
|
2018-06-14 02:15:55 +02:00
|
|
|
|
<br>
|
2018-06-13 03:51:15 +02:00
|
|
|
|
|
2018-06-13 04:04:46 +02:00
|
|
|
|
The goal of this repository is to document known and unknown methods of
|
|
|
|
|
doing various tasks using only built-in bash features. Using the snippets
|
|
|
|
|
from this guide can help to remove unneeded dependencies from your scripts
|
|
|
|
|
and in most cases make them that little bit faster. I came across these
|
|
|
|
|
tips and discovered a few while developing
|
|
|
|
|
[neofetch](https://github.com/dylanaraps/neofetch),
|
|
|
|
|
[pxltrm](https://github.com/dylanaraps/pxltrm) and some other smaller
|
|
|
|
|
projects.
|
|
|
|
|
|
2018-06-15 03:40:50 +02:00
|
|
|
|
The snippets below are linted using `shellcheck` and tests have been
|
|
|
|
|
written where applicable. If you're looking to contribute, have a read of
|
|
|
|
|
the
|
|
|
|
|
[CONTRIBUTING.md](https://github.com/dylanaraps/pure-bash-bible/blob/master/CONTRIBUTING.md).
|
|
|
|
|
It outlines how the unit tests work and what's required when adding
|
|
|
|
|
snippets.
|
|
|
|
|
|
|
|
|
|
If you see something that is incorrectly described, buggy or outright
|
|
|
|
|
wrong, open an issue or send a pull request. If you know a handy snippet
|
|
|
|
|
that is not included in this list, contribute!
|
2018-06-13 04:04:46 +02:00
|
|
|
|
|
2018-06-13 04:39:10 +02:00
|
|
|
|
**NOTE**: Error handling (*checking if a file exists, etc*) is not
|
|
|
|
|
included. These are meant to be snippets you can incorporate into your
|
|
|
|
|
scripts and not full blown utilities.
|
|
|
|
|
|
2018-06-14 02:10:08 +02:00
|
|
|
|
<br>
|
2018-06-13 04:04:46 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
# Table of Contents
|
2018-06-13 03:43:02 +02:00
|
|
|
|
|
|
|
|
|
<!-- vim-markdown-toc GFM -->
|
|
|
|
|
|
2018-06-13 04:35:53 +02:00
|
|
|
|
* [Strings](#strings)
|
2018-06-14 10:22:40 +02:00
|
|
|
|
* [Trim leading and trailing white-space from string.](#trim-leading-and-trailing-white-space-from-string)
|
|
|
|
|
* [Trim all white-space from string and truncate spaces.](#trim-all-white-space-from-string-and-truncate-spaces)
|
2018-06-15 00:26:06 +02:00
|
|
|
|
* [Use REGEX on a string.](#use-regex-on-a-string)
|
2018-06-13 05:17:25 +02:00
|
|
|
|
* [Split a string on a delimiter.](#split-a-string-on-a-delimiter)
|
2018-06-13 04:54:50 +02:00
|
|
|
|
* [Change a string to lowercase.](#change-a-string-to-lowercase)
|
|
|
|
|
* [Change a string to uppercase.](#change-a-string-to-uppercase)
|
2018-06-13 04:35:53 +02:00
|
|
|
|
* [Trim quotes from a string.](#trim-quotes-from-a-string)
|
2018-06-15 00:43:35 +02:00
|
|
|
|
* [Strip all instances of pattern from string.](#strip-all-instances-of-pattern-from-string)
|
|
|
|
|
* [Strip first occurrence of pattern from string.](#strip-first-occurrence-of-pattern-from-string)
|
|
|
|
|
* [Strip pattern from start of string.](#strip-pattern-from-start-of-string)
|
|
|
|
|
* [Strip pattern from end of string.](#strip-pattern-from-end-of-string)
|
2018-06-15 08:59:40 +02:00
|
|
|
|
* [Check if string contains a sub-string.](#check-if-string-contains-a-sub-string)
|
|
|
|
|
* [Check if string starts with sub-string.](#check-if-string-starts-with-sub-string)
|
|
|
|
|
* [Check if string ends with sub-string.](#check-if-string-ends-with-sub-string)
|
2018-06-14 06:32:13 +02:00
|
|
|
|
* [Variables](#variables)
|
|
|
|
|
* [Assign and access a variable using a variable.](#assign-and-access-a-variable-using-a-variable)
|
2018-06-18 00:52:22 +02:00
|
|
|
|
* [Indirection](#indirection)
|
|
|
|
|
* [Replacement](#replacement)
|
|
|
|
|
* [Length](#length)
|
|
|
|
|
* [Expansion](#expansion)
|
|
|
|
|
* [Case Modification](#case-modification)
|
|
|
|
|
* [Default Value](#default-value)
|
2018-06-13 04:44:07 +02:00
|
|
|
|
* [Arrays](#arrays)
|
|
|
|
|
* [Reverse an array.](#reverse-an-array)
|
2018-06-14 00:34:43 +02:00
|
|
|
|
* [Remove duplicate array elements.](#remove-duplicate-array-elements)
|
2018-06-13 23:55:13 +02:00
|
|
|
|
* [Cycle through an array.](#cycle-through-an-array)
|
|
|
|
|
* [Toggle between two values.](#toggle-between-two-values)
|
2018-06-15 08:31:06 +02:00
|
|
|
|
* [Loops](#loops)
|
|
|
|
|
* [Loop over a range of numbers.](#loop-over-a-range-of-numbers)
|
2018-06-15 08:35:22 +02:00
|
|
|
|
* [Loop over a variable range of numbers.](#loop-over-a-variable-range-of-numbers)
|
2018-06-15 08:31:06 +02:00
|
|
|
|
* [Loop over an array.](#loop-over-an-array)
|
2018-06-15 08:35:22 +02:00
|
|
|
|
* [Loop over an array with an index.](#loop-over-an-array-with-an-index)
|
2018-06-15 08:40:31 +02:00
|
|
|
|
* [Loop over the contents of a file.](#loop-over-the-contents-of-a-file)
|
2018-06-15 08:31:06 +02:00
|
|
|
|
* [Loop over files and directories.](#loop-over-files-and-directories)
|
2018-06-13 06:23:18 +02:00
|
|
|
|
* [File handling](#file-handling)
|
|
|
|
|
* [Read a file to a string.](#read-a-file-to-a-string)
|
|
|
|
|
* [Read a file to an array (*by line*).](#read-a-file-to-an-array-by-line)
|
|
|
|
|
* [Get the first N lines of a file.](#get-the-first-n-lines-of-a-file)
|
|
|
|
|
* [Get the last N lines of a file.](#get-the-last-n-lines-of-a-file)
|
|
|
|
|
* [Get the number of lines in a file.](#get-the-number-of-lines-in-a-file)
|
|
|
|
|
* [Count files or directories in directory.](#count-files-or-directories-in-directory)
|
|
|
|
|
* [Create an empty file.](#create-an-empty-file)
|
2018-06-13 05:21:42 +02:00
|
|
|
|
* [File Paths](#file-paths)
|
|
|
|
|
* [Get the directory name of a file path.](#get-the-directory-name-of-a-file-path)
|
|
|
|
|
* [Get the base-name of a file path.](#get-the-base-name-of-a-file-path)
|
2018-06-13 23:47:16 +02:00
|
|
|
|
* [Arithmetic](#arithmetic)
|
|
|
|
|
* [Simpler syntax to set variables.](#simpler-syntax-to-set-variables)
|
|
|
|
|
* [Ternary tests.](#ternary-tests)
|
2018-06-18 01:00:59 +02:00
|
|
|
|
* [Brace Expansion](#brace-expansion)
|
|
|
|
|
* [Ranges](#ranges)
|
|
|
|
|
* [String Lists](#string-lists)
|
2018-06-16 14:33:29 +02:00
|
|
|
|
* [Obsolete Syntax](#obsolete-syntax)
|
|
|
|
|
* [Shebang.](#shebang)
|
|
|
|
|
* [Command Substitution.](#command-substitution)
|
|
|
|
|
* [Function Declaration.](#function-declaration)
|
2018-06-14 01:24:29 +02:00
|
|
|
|
* [Internal Variables](#internal-variables)
|
2018-06-14 07:11:17 +02:00
|
|
|
|
* [Get the location to the `bash` binary.](#get-the-location-to-the-bash-binary)
|
|
|
|
|
* [Get the version of the current running `bash` process.](#get-the-version-of-the-current-running-bash-process)
|
|
|
|
|
* [Open the user's preferred text editor.](#open-the-users-preferred-text-editor)
|
|
|
|
|
* [Get the name of the current function.](#get-the-name-of-the-current-function)
|
|
|
|
|
* [Get the host-name of the system.](#get-the-host-name-of-the-system)
|
|
|
|
|
* [Get the architecture of the Operating System.](#get-the-architecture-of-the-operating-system)
|
|
|
|
|
* [Get the name of the Operating System / Kernel.](#get-the-name-of-the-operating-system--kernel)
|
|
|
|
|
* [Get the current working directory.](#get-the-current-working-directory)
|
|
|
|
|
* [Get the number of seconds the script has been running.](#get-the-number-of-seconds-the-script-has-been-running)
|
2018-06-16 14:33:29 +02:00
|
|
|
|
* [Information about the terminal](#information-about-the-terminal)
|
|
|
|
|
* [Get the terminal size in lines and columns (*from a script*).](#get-the-terminal-size-in-lines-and-columns-from-a-script)
|
|
|
|
|
* [Get the terminal size in pixels.](#get-the-terminal-size-in-pixels)
|
|
|
|
|
* [Get the current cursor position.](#get-the-current-cursor-position)
|
|
|
|
|
* [Colors](#colors)
|
|
|
|
|
* [Convert a hex color to RGB.](#convert-a-hex-color-to-rgb)
|
|
|
|
|
* [Convert an RGB color to hex.](#convert-an-rgb-color-to-hex)
|
|
|
|
|
* [Code Golf](#code-golf)
|
|
|
|
|
* [Shorter `for` loop syntax.](#shorter-for-loop-syntax)
|
|
|
|
|
* [Shorter infinite loops.](#shorter-infinite-loops)
|
|
|
|
|
* [Shorter function declaration.](#shorter-function-declaration)
|
|
|
|
|
* [Shorter `if` syntax.](#shorter-if-syntax)
|
|
|
|
|
* [Simpler `case` statement to set variable.](#simpler-case-statement-to-set-variable)
|
2018-06-14 07:12:45 +02:00
|
|
|
|
* [Other](#other)
|
2018-06-15 09:24:12 +02:00
|
|
|
|
* [Use `read` as an alternative to the `sleep` command.](#use-read-as-an-alternative-to-the-sleep-command)
|
2018-06-15 08:10:23 +02:00
|
|
|
|
* [Check if a program is in the user's PATH.](#check-if-a-program-is-in-the-users-path)
|
2018-06-14 07:12:45 +02:00
|
|
|
|
* [Get the current date using `strftime`.](#get-the-current-date-using-strftime)
|
2018-06-15 10:33:01 +02:00
|
|
|
|
* [Progress bars.](#progress-bars)
|
2018-06-18 01:25:38 +02:00
|
|
|
|
* [Get the list of functions from your script.](#get-the-list-of-functions-from-your-script)
|
2018-06-14 07:12:45 +02:00
|
|
|
|
* [Bypass shell aliases.](#bypass-shell-aliases)
|
|
|
|
|
* [Bypass shell functions.](#bypass-shell-functions)
|
2018-06-13 03:43:02 +02:00
|
|
|
|
|
|
|
|
|
<!-- vim-markdown-toc -->
|
2018-06-13 03:39:17 +02:00
|
|
|
|
|
2018-06-16 15:04:54 +02:00
|
|
|
|
<br>
|
2018-06-13 06:23:18 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
# Strings
|
2018-06-13 06:23:18 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Trim leading and trailing white-space from string.
|
2018-06-13 06:23:18 +02:00
|
|
|
|
|
2018-06-15 03:40:50 +02:00
|
|
|
|
This is an alternative to `sed`, `awk`, `perl` and other tools. The
|
|
|
|
|
function below works by finding all leading and trailing white-space and
|
|
|
|
|
removing it from the start and end of the string. The `:` built-in is used
|
|
|
|
|
in place of a temporary variable.
|
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-14 10:22:40 +02:00
|
|
|
|
```sh
|
2018-06-14 11:21:41 +02:00
|
|
|
|
trim_string() {
|
|
|
|
|
# Usage: trim_string " example string "
|
2018-06-14 10:22:40 +02:00
|
|
|
|
: "${1#"${1%%[![:space:]]*}"}"
|
|
|
|
|
: "${_%"${_##*[![:space:]]}"}"
|
|
|
|
|
printf '%s\n' "$_"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ trim_string " Hello, World "
|
|
|
|
|
Hello, World
|
|
|
|
|
|
|
|
|
|
$ name=" John Black "
|
|
|
|
|
$ trim_string "$name"
|
|
|
|
|
John Black
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Trim all white-space from string and truncate spaces.
|
2018-06-13 06:23:18 +02:00
|
|
|
|
|
2018-06-15 03:40:50 +02:00
|
|
|
|
This is an alternative to `sed`, `awk`, `perl` and other tools. The
|
|
|
|
|
function below works by abusing word splitting to create a new string
|
|
|
|
|
without leading/trailing white-space and with truncated spaces.
|
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-13 06:23:18 +02:00
|
|
|
|
```sh
|
|
|
|
|
# shellcheck disable=SC2086,SC2048
|
2018-06-14 11:21:41 +02:00
|
|
|
|
trim_all() {
|
2018-06-15 01:29:43 +02:00
|
|
|
|
# Usage: trim_all " example string "
|
2018-06-13 06:23:18 +02:00
|
|
|
|
set -f
|
|
|
|
|
set -- $*
|
|
|
|
|
printf '%s\n' "$*"
|
|
|
|
|
set +f
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ trim_all " Hello, World "
|
|
|
|
|
Hello, World
|
|
|
|
|
|
|
|
|
|
$ name=" John Black is my name. "
|
|
|
|
|
$ trim_all "$name"
|
|
|
|
|
John Black is my name.
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Use REGEX on a string.
|
2018-06-15 00:26:06 +02:00
|
|
|
|
|
2018-06-15 03:40:50 +02:00
|
|
|
|
We can use the result of `bash`'s regex matching to replace `sed` for a
|
|
|
|
|
large number of use-cases.
|
2018-06-15 00:26:06 +02:00
|
|
|
|
|
2018-06-15 03:40:50 +02:00
|
|
|
|
**CAVEAT**: This is one of the few platform dependant `bash` features.
|
2018-06-15 00:26:06 +02:00
|
|
|
|
`bash` will use whatever regex engine is installed on the user's system.
|
|
|
|
|
Stick to POSIX regex features if aiming for compatibility.
|
|
|
|
|
|
2018-06-15 03:40:50 +02:00
|
|
|
|
**CAVEAT**: This example only prints the first matching group. When using
|
2018-06-15 04:02:28 +02:00
|
|
|
|
multiple capture groups some modification is needed.
|
2018-06-15 00:26:06 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-15 00:26:06 +02:00
|
|
|
|
```sh
|
|
|
|
|
regex() {
|
|
|
|
|
# Usage: regex "string" "regex"
|
|
|
|
|
[[ $1 =~ $2 ]] && printf '%s\n' "${BASH_REMATCH[1]}"
|
|
|
|
|
}
|
2018-06-15 01:29:43 +02:00
|
|
|
|
```
|
2018-06-15 00:26:06 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Usage:**
|
2018-06-15 00:26:06 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
```shell
|
|
|
|
|
$ # Trim leading white-space.
|
|
|
|
|
$ regex ' hello' '^\s*(.*)'
|
|
|
|
|
hello
|
2018-06-15 00:26:06 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
$ # Validate a hex color.
|
|
|
|
|
$ regex "#FFFFFF" '^(#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3}))$'
|
|
|
|
|
#FFFFFF
|
|
|
|
|
|
|
|
|
|
$ # Validate a hex color (invalid).
|
|
|
|
|
$ regex "red" '^(#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3}))$'
|
|
|
|
|
# no output (invalid)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Example Usage in script:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
is_hex_color() {
|
|
|
|
|
if [[ "$1" =~ ^(#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3}))$ ]]; then
|
|
|
|
|
printf '%s\n' "${BASH_REMATCH[1]}"
|
|
|
|
|
else
|
|
|
|
|
printf '%s\n' "error: $1 is an invalid color."
|
|
|
|
|
return 1
|
|
|
|
|
fi
|
2018-06-15 00:26:06 +02:00
|
|
|
|
}
|
2018-06-15 01:29:43 +02:00
|
|
|
|
|
|
|
|
|
read -r color
|
|
|
|
|
is_hex_color "$color" || color="#FFFFFF"
|
|
|
|
|
|
|
|
|
|
# Do stuff.
|
2018-06-15 00:26:06 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Split a string on a delimiter.
|
2018-06-13 06:23:18 +02:00
|
|
|
|
|
2018-06-15 03:40:50 +02:00
|
|
|
|
This is an alternative to `cut`, `awk` and other tools.
|
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
```shell
|
|
|
|
|
string="1,2,3"
|
2018-06-13 06:23:18 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
# To multiple variables.
|
|
|
|
|
IFS=, read -r var1 var2 var3 <<< "$string"
|
|
|
|
|
|
|
|
|
|
# To an array.
|
|
|
|
|
IFS=, read -ra vars <<< "$string"
|
2018-06-13 06:23:18 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Change a string to lowercase.
|
2018-06-13 06:23:18 +02:00
|
|
|
|
|
2018-06-15 03:40:50 +02:00
|
|
|
|
**CAVEAT:** Requires `bash` 4+
|
2018-06-13 06:23:18 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-13 06:23:18 +02:00
|
|
|
|
```sh
|
|
|
|
|
lower() {
|
|
|
|
|
# Usage: lower "string"
|
|
|
|
|
printf '%s\n' "${1,,}"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ lower "HELLO"
|
|
|
|
|
hello
|
|
|
|
|
|
|
|
|
|
$ lower "HeLlO"
|
|
|
|
|
hello
|
|
|
|
|
|
|
|
|
|
$ lower "hello"
|
|
|
|
|
hello
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Change a string to uppercase.
|
2018-06-13 06:23:18 +02:00
|
|
|
|
|
2018-06-15 03:40:50 +02:00
|
|
|
|
**CAVEAT:** Requires `bash` 4+
|
2018-06-13 06:23:18 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-13 06:23:18 +02:00
|
|
|
|
```sh
|
|
|
|
|
upper() {
|
|
|
|
|
# Usage: upper "string"
|
|
|
|
|
printf '%s\n' "${1^^}"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ upper "hello"
|
|
|
|
|
HELLO
|
|
|
|
|
|
|
|
|
|
$ upper "HeLlO"
|
|
|
|
|
HELLO
|
|
|
|
|
|
|
|
|
|
$ upper "HELLO"
|
|
|
|
|
HELLO
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Trim quotes from a string.
|
2018-06-13 06:23:18 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-13 06:23:18 +02:00
|
|
|
|
```sh
|
|
|
|
|
trim_quotes() {
|
|
|
|
|
# Usage: trim_quotes "string"
|
|
|
|
|
: "${1//\'}"
|
|
|
|
|
printf "%s\\n" "${_//\"}"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ var="'Hello', \"World\""
|
|
|
|
|
$ trim_quotes "$var"
|
|
|
|
|
Hello, World
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Strip all instances of pattern from string.
|
2018-06-15 00:43:35 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-15 00:43:35 +02:00
|
|
|
|
```sh
|
|
|
|
|
strip_all() {
|
|
|
|
|
# Usage: strip_all "string" "pattern"
|
|
|
|
|
printf '%s\n' "${1//$2}"
|
|
|
|
|
}
|
2018-06-15 01:29:43 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Example Usage:**
|
2018-06-15 00:43:35 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
```shell
|
|
|
|
|
$ strip_all "The Quick Brown Fox" "[aeiou]"
|
|
|
|
|
Th Qck Brwn Fx
|
2018-06-15 00:43:35 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
$ strip_all "The Quick Brown Fox" "[[:space:]]"
|
|
|
|
|
TheQuickBrownFox
|
2018-06-15 00:43:35 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
$ strip_all "The Quick Brown Fox" "Quick "
|
|
|
|
|
The Brown Fox
|
2018-06-15 00:43:35 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Strip first occurrence of pattern from string.
|
2018-06-15 00:43:35 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-15 00:43:35 +02:00
|
|
|
|
```sh
|
|
|
|
|
strip() {
|
|
|
|
|
# Usage: strip "string" "pattern"
|
|
|
|
|
printf '%s\n' "${1/$2}"
|
|
|
|
|
}
|
2018-06-15 01:29:43 +02:00
|
|
|
|
```
|
2018-06-15 00:43:35 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Usage:**
|
2018-06-15 00:43:35 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
```shell
|
|
|
|
|
$ strip "The Quick Brown Fox" "[aeiou]"
|
|
|
|
|
Th Quick Brown Fox
|
2018-06-15 00:43:35 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
$ strip "The Quick Brown Fox" "[[:space:]]"
|
|
|
|
|
TheQuick Brown Fox
|
2018-06-15 00:43:35 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Strip pattern from start of string.
|
2018-06-13 06:23:18 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-13 06:23:18 +02:00
|
|
|
|
```sh
|
|
|
|
|
lstrip() {
|
2018-06-15 00:43:35 +02:00
|
|
|
|
# Usage: lstrip "string" "pattern"
|
2018-06-13 06:23:18 +02:00
|
|
|
|
printf '%s\n' "${1##$2}"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ lstrip "The Quick Brown Fox" "The "
|
|
|
|
|
Quick Brown Fox
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Strip pattern from end of string.
|
2018-06-13 06:23:18 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-13 06:23:18 +02:00
|
|
|
|
```sh
|
|
|
|
|
rstrip() {
|
2018-06-15 00:43:35 +02:00
|
|
|
|
# Usage: rstrip "string" "pattern"
|
2018-06-13 06:23:18 +02:00
|
|
|
|
printf '%s\n' "${1%%$2}"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ rstrip "The Quick Brown Fox" " Fox"
|
|
|
|
|
The Quick Brown
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 08:59:40 +02:00
|
|
|
|
## Check if string contains a sub-string.
|
2018-06-15 07:57:17 +02:00
|
|
|
|
|
2018-06-15 08:03:05 +02:00
|
|
|
|
**Using a test:**
|
|
|
|
|
|
2018-06-15 07:57:17 +02:00
|
|
|
|
```shell
|
|
|
|
|
if [[ "$var" == *sub_string* ]]; then
|
2018-06-15 08:59:40 +02:00
|
|
|
|
printf '%s\n' "sub_string is in var."
|
2018-06-15 07:57:17 +02:00
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Inverse (substring not in string).
|
|
|
|
|
if [[ "$var" != *sub_string* ]]; then
|
2018-06-15 08:59:40 +02:00
|
|
|
|
printf '%s\n' "sub_string is not in var."
|
2018-06-15 07:57:17 +02:00
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# This works for arrays too!
|
|
|
|
|
if [[ "${arr[*]}" == *sub_string* ]]; then
|
|
|
|
|
printf '%s\n' "sub_string is in array."
|
|
|
|
|
fi
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 08:59:40 +02:00
|
|
|
|
## Check if string starts with sub-string.
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
if [[ "$var" == sub_string* ]]; then
|
|
|
|
|
printf '%s\n' "var starts with sub_string."
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Inverse (var doesn't start with sub_string).
|
|
|
|
|
if [[ "$var" != sub_string* ]]; then
|
|
|
|
|
printf '%s\n' "var does not start with sub_string."
|
|
|
|
|
fi
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Check if string ends with sub-string.
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
if [[ "$var" == *sub_string ]]; then
|
|
|
|
|
printf '%s\n' "var ends with sub_string."
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Inverse (var doesn't start with sub_string).
|
|
|
|
|
if [[ "$var" != *sub_string ]]; then
|
|
|
|
|
printf '%s\n' "var does not end with sub_string."
|
|
|
|
|
fi
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 08:03:05 +02:00
|
|
|
|
**Using a case statement:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
case "$var" in
|
|
|
|
|
*sub_string*)
|
|
|
|
|
# Do stuff
|
|
|
|
|
;;
|
|
|
|
|
|
|
|
|
|
*sub_string2*)
|
|
|
|
|
# Do more stuff
|
|
|
|
|
;;
|
|
|
|
|
|
|
|
|
|
*)
|
|
|
|
|
# Else
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
# Variables
|
2018-06-14 06:32:13 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Assign and access a variable using a variable.
|
2018-06-14 06:32:13 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
```shell
|
|
|
|
|
hello_world="test"
|
2018-06-14 13:02:16 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
# Create the variable name.
|
|
|
|
|
var1="world"
|
|
|
|
|
var2="hello_${var1}"
|
2018-06-14 06:36:57 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
# Print the value of the variable name stored in 'hello_$var1'.
|
|
|
|
|
printf '%s\n' "${!var2}"
|
2018-06-14 06:32:13 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-18 00:52:22 +02:00
|
|
|
|
## Indirection
|
|
|
|
|
|
|
|
|
|
| Parameter | What does it do? |
|
|
|
|
|
| --------- | ---------------- |
|
|
|
|
|
| `${!VAR}` | Access a variable based on the value of `VAR`. See: [link](#assign-and-access-a-variable-using-a-variable)
|
|
|
|
|
| `${!VAR*}` | Expand to `IFS` separated list of variable names starting with `VAR`. |
|
|
|
|
|
| `${!VAR@}` | Expand to `IFS` separated list of variable names starting with `VAR`. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Replacement
|
|
|
|
|
|
|
|
|
|
| Parameter | What does it do? |
|
|
|
|
|
| --------- | ---------------- |
|
|
|
|
|
| `${VAR#PATTERN}` | Remove shortest match of pattern from start of string. |
|
|
|
|
|
| `${VAR##PATTERN}` | Remove longest match of pattern from start of string. |
|
|
|
|
|
| `${VAR%PATTERN}` | Remove shortest match of pattern from end of string. |
|
|
|
|
|
| `${VAR%%PATTERN}` | Remove longest match of pattern from end of string. |
|
|
|
|
|
| `${VAR/PATTERN/REPLACE}` | Replace first match with string.
|
|
|
|
|
| `${VAR//PATTERN/REPLACE}` | Replace all matches with string.
|
|
|
|
|
| `${VAR/PATTERN}` | Remove first match.
|
|
|
|
|
| `${VAR//PATTERN}` | Remove all matches.
|
|
|
|
|
|
|
|
|
|
## Length
|
|
|
|
|
|
|
|
|
|
| Parameter | What does it do? |
|
|
|
|
|
| --------- | ---------------- |
|
|
|
|
|
| `${#VAR}` | Length of var in characters.
|
|
|
|
|
| `${#ARR[@]}` | Length of array in elements.
|
|
|
|
|
|
|
|
|
|
## Expansion
|
|
|
|
|
|
|
|
|
|
| Parameter | What does it do? |
|
|
|
|
|
| --------- | ---------------- |
|
|
|
|
|
| `${VAR:OFFSET}` | Remove first `N` chars from variable.
|
|
|
|
|
| `${VAR:OFFSET:LENGTH}` | Get substring from `N` character to `N` character. <br> (`${VAR:10:10}`: Get sub-string from char `10` to char `20`)
|
|
|
|
|
| `${VAR:: OFFSET}` | Get first `N` chars from variable.
|
|
|
|
|
| `${VAR:: -OFFSET}` | Remove last `N` chars from variable.
|
|
|
|
|
| `${VAR: -OFFSET}` | Get last `N` chars from variable.
|
|
|
|
|
| `${VAR:OFFSET:-OFFSET}` | Cut first `N` chars and last `N` chars. | `bash 4.2+` |
|
|
|
|
|
|
|
|
|
|
## Case Modification
|
|
|
|
|
|
|
|
|
|
| Parameter | What does it do? | CAVEAT |
|
|
|
|
|
| --------- | ---------------- | ------ |
|
|
|
|
|
| `${VAR^}` | Uppercase first character. | `bash 4+` |
|
|
|
|
|
| `${VAR^^}` | Uppercase all characters. | `bash 4+` |
|
|
|
|
|
| `${VAR,}` | Lowercase first character. | `bash 4+` |
|
|
|
|
|
| `${VAR,,}` | Lowercase all characters. | `bash 4+` |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Default Value
|
|
|
|
|
|
|
|
|
|
| Parameter | What does it do? |
|
|
|
|
|
| --------- | ---------------- |
|
|
|
|
|
| `${VAR:-STRING}` | If `VAR` is empty or unset, use `STRING` as it's value.
|
|
|
|
|
| `${VAR-STRING}` | If `VAR` is unset, use `STRING` as it's value.
|
|
|
|
|
| `${VAR:=STRING}` | If `VAR` is empty or unset, set the value of `VAR` to `STRING`.
|
|
|
|
|
| `${VAR=STRING}` | If `VAR` is unset, set the value of `VAR` to `STRING`.
|
|
|
|
|
| `${VAR:+STRING}` | If `VAR` isn't empty, use `STRING` as it's value.
|
|
|
|
|
| `${VAR+STRING}` | If `VAR` is set, use `STRING` as it's value.
|
|
|
|
|
| `${VAR:?STRING}` | Display an error if empty or unset.
|
|
|
|
|
| `${VAR?STRING}` | Display an error if unset.
|
|
|
|
|
|
2018-06-14 06:32:13 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
# Arrays
|
2018-06-13 06:23:18 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Reverse an array.
|
2018-06-13 06:23:18 +02:00
|
|
|
|
|
|
|
|
|
Enabling `extdebug` allows access to the `BASH_ARGV` array which stores
|
|
|
|
|
the current function’s arguments in reverse.
|
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-13 06:23:18 +02:00
|
|
|
|
```sh
|
|
|
|
|
reverse_array() {
|
|
|
|
|
# Usage: reverse_array "array"
|
|
|
|
|
shopt -s extdebug
|
2018-06-14 11:39:26 +02:00
|
|
|
|
f()(printf '%s\n' "${BASH_ARGV[@]}"); f "$@"
|
2018-06-13 06:23:18 +02:00
|
|
|
|
shopt -u extdebug
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ reverse_array 1 2 3 4 5
|
|
|
|
|
5
|
|
|
|
|
4
|
|
|
|
|
3
|
|
|
|
|
2
|
|
|
|
|
1
|
|
|
|
|
|
|
|
|
|
$ arr=(red blue green)
|
|
|
|
|
$ reverse_array "${arr[@]}"
|
|
|
|
|
green
|
|
|
|
|
blue
|
|
|
|
|
red
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Remove duplicate array elements.
|
2018-06-14 00:34:43 +02:00
|
|
|
|
|
|
|
|
|
Create a temporary associative array. When setting associative array
|
|
|
|
|
values and a duplicate assignment occurs, bash overwrites the key. This
|
|
|
|
|
allows us to effectively remove array duplicates.
|
|
|
|
|
|
2018-06-15 03:40:50 +02:00
|
|
|
|
**CAVEAT:** Requires `bash` 4+
|
2018-06-14 00:35:20 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-14 00:34:43 +02:00
|
|
|
|
```sh
|
|
|
|
|
remove_array_dups() {
|
|
|
|
|
# Usage: remove_array_dups "array"
|
|
|
|
|
declare -A tmp_array
|
|
|
|
|
|
|
|
|
|
for i in "$@"; do
|
|
|
|
|
[[ "$i" ]] && IFS=" " tmp_array["${i:- }"]=1
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
printf '%s\n' "${!tmp_array[@]}"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:53:22 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
```shell
|
|
|
|
|
$ remove_array_dups 1 1 2 2 3 3 3 3 3 4 4 4 4 4 5 5 5 5 5 5
|
|
|
|
|
1
|
|
|
|
|
2
|
|
|
|
|
3
|
|
|
|
|
4
|
|
|
|
|
5
|
|
|
|
|
|
|
|
|
|
$ arr=(red red green blue blue)
|
|
|
|
|
$ remove_array_dups "${arr[@]}"
|
|
|
|
|
red
|
|
|
|
|
green
|
|
|
|
|
blue
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Cycle through an array.
|
2018-06-13 23:55:13 +02:00
|
|
|
|
|
|
|
|
|
Each time the `printf` is called, the next array element is printed. When
|
|
|
|
|
the print hits the last array element it starts from the first element
|
|
|
|
|
again.
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
arr=(a b c d)
|
2018-06-14 09:50:46 +02:00
|
|
|
|
|
|
|
|
|
cycle() {
|
|
|
|
|
printf '%s ' "${arr[${i:=0}]}"
|
|
|
|
|
((i=i>=${#arr[@]}-1?0:++i))
|
|
|
|
|
}
|
2018-06-13 23:55:13 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Toggle between two values.
|
2018-06-13 23:55:13 +02:00
|
|
|
|
|
|
|
|
|
This works the same as above, this is just a different use case.
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
arr=(true false)
|
2018-06-14 09:50:46 +02:00
|
|
|
|
|
|
|
|
|
cycle() {
|
|
|
|
|
printf '%s ' "${arr[${i:=0}]}"
|
|
|
|
|
((i=i>=${#arr[@]}-1?0:++i))
|
|
|
|
|
}
|
2018-06-13 23:55:13 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 08:31:06 +02:00
|
|
|
|
# Loops
|
|
|
|
|
|
|
|
|
|
## Loop over a range of numbers.
|
|
|
|
|
|
|
|
|
|
Don't use `seq`.
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
# Loop from 0-100 (no variable support).
|
|
|
|
|
for i in {0..100}; do
|
|
|
|
|
printf '%s\n' "$i"
|
|
|
|
|
done
|
2018-06-15 08:35:22 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Loop over a variable range of numbers.
|
|
|
|
|
|
|
|
|
|
Don't use `seq`.
|
2018-06-15 08:31:06 +02:00
|
|
|
|
|
2018-06-15 08:35:22 +02:00
|
|
|
|
```shell
|
2018-06-15 08:31:06 +02:00
|
|
|
|
# Loop from 0-VAR.
|
|
|
|
|
VAR=50
|
|
|
|
|
for ((i=0;i<=VAR;i++)); do
|
|
|
|
|
printf '%s\n' "$i"
|
|
|
|
|
done
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Loop over an array.
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
arr=(apples oranges tomatoes)
|
|
|
|
|
|
|
|
|
|
# Just elements.
|
|
|
|
|
for element in "${arr[@]}"; do
|
|
|
|
|
printf '%s\n' "$element"
|
|
|
|
|
done
|
2018-06-15 08:35:22 +02:00
|
|
|
|
```
|
2018-06-15 08:31:06 +02:00
|
|
|
|
|
2018-06-15 08:35:22 +02:00
|
|
|
|
## Loop over an array with an index.
|
|
|
|
|
|
|
|
|
|
```shell
|
2018-06-15 08:43:03 +02:00
|
|
|
|
arr=(apples oranges tomatoes)
|
|
|
|
|
|
2018-06-15 08:31:06 +02:00
|
|
|
|
# Elements and index.
|
|
|
|
|
for i in "${!arr[@]}"; do
|
|
|
|
|
printf '%s\n' "${arr[$i]}"
|
|
|
|
|
done
|
2018-06-15 08:43:03 +02:00
|
|
|
|
|
|
|
|
|
# Alternative method.
|
|
|
|
|
for ((i=0;i<${#arr[@]};i++)); do
|
|
|
|
|
printf '%s\n' "${arr[$i]}"
|
|
|
|
|
done
|
2018-06-15 08:31:06 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 08:40:31 +02:00
|
|
|
|
## Loop over the contents of a file.
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
while read -r line; do
|
|
|
|
|
printf '%s\n' "$line"
|
|
|
|
|
done < "file"
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 08:31:06 +02:00
|
|
|
|
## Loop over files and directories.
|
|
|
|
|
|
|
|
|
|
Don’t use `ls`.
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
# Greedy example.
|
|
|
|
|
for file in *; do
|
|
|
|
|
printf '%s\n' "$file"
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# PNG files in dir.
|
|
|
|
|
for file in ~/Pictures/*.png; do
|
|
|
|
|
printf '%s\n' "$file"
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# Iterate over directories.
|
|
|
|
|
for dir in ~/Downloads/*/; do
|
|
|
|
|
printf '%s\n' "$dir"
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# Iterate recursively.
|
|
|
|
|
shopt -s globstar
|
|
|
|
|
for file in ~/Pictures/**/*; do
|
|
|
|
|
printf '%s\n' "$file"
|
|
|
|
|
done
|
|
|
|
|
shopt -u globstar
|
|
|
|
|
```
|
2018-06-13 23:55:13 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
# File handling
|
2018-06-13 03:39:17 +02:00
|
|
|
|
|
2018-06-17 02:52:18 +02:00
|
|
|
|
**CAVEAT:** `bash` doesn't handle binary data properly in versions `< 4.4`.
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Read a file to a string.
|
2018-06-13 04:51:35 +02:00
|
|
|
|
|
|
|
|
|
Alternative to the `cat` command.
|
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
```shell
|
|
|
|
|
file_data="$(<"file")"
|
2018-06-13 04:51:35 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Read a file to an array (*by line*).
|
2018-06-13 04:51:35 +02:00
|
|
|
|
|
|
|
|
|
Alternative to the `cat` command.
|
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
```shell
|
|
|
|
|
# Bash <4
|
|
|
|
|
IFS=$'\n' read -d "" -ra file_data < "file"
|
2018-06-14 01:40:34 +02:00
|
|
|
|
|
2018-06-15 01:29:43 +02:00
|
|
|
|
# Bash 4+
|
|
|
|
|
mapfile -t file_data < "file"
|
2018-06-13 04:51:35 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Get the first N lines of a file.
|
2018-06-13 04:22:42 +02:00
|
|
|
|
|
|
|
|
|
Alternative to the `head` command.
|
|
|
|
|
|
2018-06-15 03:40:50 +02:00
|
|
|
|
**CAVEAT:** Requires `bash` 4+
|
2018-06-13 04:22:42 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-13 04:22:42 +02:00
|
|
|
|
```sh
|
|
|
|
|
head() {
|
|
|
|
|
# Usage: head "n" "file"
|
|
|
|
|
mapfile -tn "$1" line < "$2"
|
|
|
|
|
printf '%s\n' "${line[@]}"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ head 2 ~/.bashrc
|
|
|
|
|
# Prompt
|
|
|
|
|
PS1='➜ '
|
|
|
|
|
|
|
|
|
|
$ head 1 ~/.bashrc
|
|
|
|
|
# Prompt
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Get the last N lines of a file.
|
2018-06-13 04:22:42 +02:00
|
|
|
|
|
|
|
|
|
Alternative to the `tail` command.
|
|
|
|
|
|
2018-06-15 03:40:50 +02:00
|
|
|
|
**CAVEAT:** Requires `bash` 4+
|
2018-06-13 04:22:42 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-13 04:22:42 +02:00
|
|
|
|
```sh
|
|
|
|
|
tail() {
|
|
|
|
|
# Usage: tail "n" "file"
|
|
|
|
|
mapfile -tn 0 line < "$2"
|
|
|
|
|
printf '%s\n' "${line[@]: -$1}"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:51:01 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ tail 2 ~/.bashrc
|
|
|
|
|
# Enable tmux.
|
|
|
|
|
# [[ -z "$TMUX" ]] && exec tmux
|
|
|
|
|
|
|
|
|
|
$ tail 1 ~/.bashrc
|
|
|
|
|
# [[ -z "$TMUX" ]] && exec tmux
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Get the number of lines in a file.
|
2018-06-13 04:31:24 +02:00
|
|
|
|
|
|
|
|
|
Alternative to `wc -l`.
|
|
|
|
|
|
2018-06-16 01:09:56 +02:00
|
|
|
|
**Example Function (bash 4):**
|
2018-06-15 01:37:35 +02:00
|
|
|
|
|
2018-06-13 04:31:24 +02:00
|
|
|
|
```sh
|
|
|
|
|
lines() {
|
2018-06-16 01:19:11 +02:00
|
|
|
|
# Usage: lines "file"
|
2018-06-13 04:31:24 +02:00
|
|
|
|
mapfile -tn 0 lines < "$1"
|
|
|
|
|
printf '%s\n' "${#lines[@]}"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-16 01:09:56 +02:00
|
|
|
|
**Example Function (bash 3):**
|
|
|
|
|
|
|
|
|
|
This method uses less memory than the `mapfile` method and it's more
|
|
|
|
|
compatible but it's slower for bigger files.
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
lines_loop() {
|
2018-06-16 01:19:11 +02:00
|
|
|
|
# Usage: lines_loop "file"
|
2018-06-16 01:09:56 +02:00
|
|
|
|
count=0
|
|
|
|
|
while IFS= read -r _; do
|
|
|
|
|
((count++))
|
|
|
|
|
done < "$1"
|
|
|
|
|
printf '%s\n' "$count"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:51:01 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ lines ~/.bashrc
|
|
|
|
|
48
|
2018-06-16 01:09:56 +02:00
|
|
|
|
|
|
|
|
|
$ lines_loop ~/.bashrc
|
|
|
|
|
48
|
2018-06-15 01:51:01 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Count files or directories in directory.
|
2018-06-13 04:35:53 +02:00
|
|
|
|
|
2018-06-13 06:23:18 +02:00
|
|
|
|
This works by passing the output of the glob as function arguments. We
|
|
|
|
|
then count the arguments and print the number.
|
2018-06-13 05:42:29 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-13 05:42:29 +02:00
|
|
|
|
```sh
|
2018-06-13 06:23:18 +02:00
|
|
|
|
count() {
|
|
|
|
|
# Usage: count /path/to/dir/*
|
|
|
|
|
# count /path/to/dir/*/
|
|
|
|
|
printf '%s\n' "$#"
|
2018-06-13 05:42:29 +02:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:51:01 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
# Count all files in dir.
|
|
|
|
|
$ count ~/Downloads/*
|
|
|
|
|
232
|
|
|
|
|
|
|
|
|
|
# Count all dirs in dir.
|
|
|
|
|
$ count ~/Downloads/*/
|
|
|
|
|
45
|
|
|
|
|
|
|
|
|
|
# Count all jpg files in dir.
|
|
|
|
|
$ count ~/Pictures/*.jpg
|
|
|
|
|
64
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Create an empty file.
|
2018-06-13 04:44:07 +02:00
|
|
|
|
|
2018-06-13 06:23:18 +02:00
|
|
|
|
Alternative to `touch`.
|
2018-06-13 04:44:07 +02:00
|
|
|
|
|
2018-06-15 01:51:01 +02:00
|
|
|
|
```shell
|
|
|
|
|
# Shortest.
|
|
|
|
|
:> file
|
2018-06-13 04:44:07 +02:00
|
|
|
|
|
2018-06-15 01:51:01 +02:00
|
|
|
|
# Longer alternatives:
|
|
|
|
|
echo -n > file
|
|
|
|
|
printf '' > file
|
2018-06-13 04:44:07 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
# File Paths
|
2018-06-13 05:21:42 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Get the directory name of a file path.
|
2018-06-13 05:21:42 +02:00
|
|
|
|
|
|
|
|
|
Alternative to the `dirname` command.
|
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-13 05:21:42 +02:00
|
|
|
|
```sh
|
|
|
|
|
dirname() {
|
|
|
|
|
# Usage: dirname "path"
|
|
|
|
|
printf '%s\n' "${1%/*}/"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:51:01 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ dirname ~/Pictures/Wallpapers/1.jpg
|
|
|
|
|
/home/black/Pictures/Wallpapers/
|
|
|
|
|
|
|
|
|
|
$ dirname ~/Pictures/Downloads/
|
|
|
|
|
/home/black/Pictures/
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Get the base-name of a file path.
|
2018-06-13 05:21:42 +02:00
|
|
|
|
|
|
|
|
|
Alternative to the `basename` command.
|
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-13 05:21:42 +02:00
|
|
|
|
```sh
|
|
|
|
|
basename() {
|
|
|
|
|
# Usage: basename "path"
|
2018-06-13 05:42:29 +02:00
|
|
|
|
: "${1%/}"
|
|
|
|
|
printf '%s\n' "${_##*/}"
|
2018-06-13 05:21:42 +02:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:51:01 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ basename ~/Pictures/Wallpapers/1.jpg
|
|
|
|
|
1.jpg
|
|
|
|
|
|
|
|
|
|
$ basename ~/Pictures/Downloads/
|
|
|
|
|
Downloads
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
# Arithmetic
|
2018-06-13 23:47:16 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Simpler syntax to set variables.
|
2018-06-13 23:47:16 +02:00
|
|
|
|
|
2018-06-15 01:51:01 +02:00
|
|
|
|
```shell
|
|
|
|
|
# Simple math
|
|
|
|
|
((var=1+2))
|
|
|
|
|
|
|
|
|
|
# Decrement/Increment variable
|
|
|
|
|
((var++))
|
|
|
|
|
((var--))
|
|
|
|
|
((var+=1))
|
|
|
|
|
((var-=1))
|
|
|
|
|
|
|
|
|
|
# Using variables
|
|
|
|
|
((var=var2*arr[2]))
|
2018-06-13 23:47:16 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Ternary tests.
|
2018-06-13 23:47:16 +02:00
|
|
|
|
|
2018-06-15 01:51:01 +02:00
|
|
|
|
```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))
|
2018-06-13 23:47:16 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-18 01:00:59 +02:00
|
|
|
|
# Brace Expansion
|
|
|
|
|
|
|
|
|
|
## Ranges
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
# Syntax: {<START>..<END>}
|
|
|
|
|
|
|
|
|
|
# Print numbers 1-100.
|
|
|
|
|
echo {1..100}
|
|
|
|
|
|
|
|
|
|
# Print range of floats.
|
|
|
|
|
echo 1.{1..9}
|
|
|
|
|
|
|
|
|
|
# Print chars a-z.
|
|
|
|
|
echo {a..z}
|
|
|
|
|
echo {A..Z}
|
|
|
|
|
|
|
|
|
|
# Nesting.
|
|
|
|
|
echo {A..Z}{0..9}
|
2018-06-18 01:03:43 +02:00
|
|
|
|
|
|
|
|
|
# Print zero-padded numbers.
|
|
|
|
|
# CAVEAT: bash 4+
|
|
|
|
|
echo {01..100}
|
|
|
|
|
|
|
|
|
|
# Change increment amount.
|
|
|
|
|
# Syntax: {<START>..<END>..<INCREMENT>}
|
|
|
|
|
# CAVEAT: bash 4+
|
|
|
|
|
echo {1..10..2} # Increment by 2.
|
2018-06-18 01:00:59 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## String Lists
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
echo {apples,oranges,pears,grapes}
|
|
|
|
|
|
|
|
|
|
# Example Usage:
|
|
|
|
|
# Remove dirs Movies, Music and ISOS from ~/Downloads/.
|
|
|
|
|
rm -rf ~/Downloads/{Movies,Music,ISOS}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-16 14:33:29 +02:00
|
|
|
|
# Obsolete Syntax
|
2018-06-13 04:35:53 +02:00
|
|
|
|
|
2018-06-16 14:33:29 +02:00
|
|
|
|
## Shebang.
|
2018-06-13 04:04:46 +02:00
|
|
|
|
|
2018-06-16 14:33:29 +02:00
|
|
|
|
Use `#!/usr/bin/env bash` instead of `#!/bin/bash`.
|
2018-06-15 01:37:35 +02:00
|
|
|
|
|
2018-06-16 14:33:29 +02:00
|
|
|
|
- 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.
|
2018-06-13 04:04:46 +02:00
|
|
|
|
|
2018-06-16 14:33:29 +02:00
|
|
|
|
```shell
|
|
|
|
|
# Right:
|
|
|
|
|
|
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
|
|
|
|
|
# Wrong:
|
|
|
|
|
|
|
|
|
|
#!/bin/bash
|
2018-06-13 04:04:46 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-16 14:33:29 +02:00
|
|
|
|
## Command Substitution.
|
|
|
|
|
|
|
|
|
|
Use `$()` instead of `` ` ` ``.
|
2018-06-15 01:51:01 +02:00
|
|
|
|
|
|
|
|
|
```shell
|
2018-06-16 14:33:29 +02:00
|
|
|
|
# Right.
|
|
|
|
|
var="$(command)"
|
|
|
|
|
|
|
|
|
|
# Wrong.
|
|
|
|
|
var=`command`
|
|
|
|
|
|
|
|
|
|
# $() can easily be nested whereas `` cannot.
|
|
|
|
|
var="$(command "$(command)")"
|
2018-06-15 01:51:01 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-16 14:33:29 +02:00
|
|
|
|
## Function Declaration.
|
2018-06-15 01:51:01 +02:00
|
|
|
|
|
2018-06-16 14:33:29 +02:00
|
|
|
|
Don't use the `function` keyword, it reduces compatibility with older versions of `bash`.
|
2018-06-13 04:04:46 +02:00
|
|
|
|
|
2018-06-16 14:33:29 +02:00
|
|
|
|
```shell
|
|
|
|
|
# Right.
|
|
|
|
|
do_something() {
|
|
|
|
|
# ...
|
|
|
|
|
}
|
2018-06-15 01:37:35 +02:00
|
|
|
|
|
2018-06-16 14:33:29 +02:00
|
|
|
|
# Wrong.
|
|
|
|
|
function do_something() {
|
|
|
|
|
# ...
|
2018-06-13 04:04:46 +02:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-16 14:33:29 +02:00
|
|
|
|
|
|
|
|
|
# Internal Variables
|
|
|
|
|
|
|
|
|
|
**NOTE**: This list does not include every internal variable (*You can
|
|
|
|
|
help by adding a missing entry!*).
|
|
|
|
|
|
|
|
|
|
For a complete list, see:
|
|
|
|
|
http://tldp.org/LDP/abs/html/internalvariables.html
|
|
|
|
|
|
|
|
|
|
## Get the location to the `bash` binary.
|
2018-06-15 01:51:01 +02:00
|
|
|
|
|
|
|
|
|
```shell
|
2018-06-16 14:33:29 +02:00
|
|
|
|
"$BASH"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Get the version of the current running `bash` process.
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
# As a string.
|
|
|
|
|
"$BASH_VERSION"
|
|
|
|
|
|
|
|
|
|
# As an array.
|
|
|
|
|
"${BASH_VERSINFO[@]}"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Open the user's preferred text editor.
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
"$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"
|
2018-06-15 01:51:01 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
# Information about the terminal
|
2018-06-13 04:35:53 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Get the terminal size in lines and columns (*from a script*).
|
2018-06-13 04:35:53 +02:00
|
|
|
|
|
|
|
|
|
This is handy when writing scripts in pure bash and `stty`/`tput` can’t be
|
|
|
|
|
called.
|
2018-06-13 03:39:17 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-13 04:21:50 +02:00
|
|
|
|
```sh
|
2018-06-13 04:35:53 +02:00
|
|
|
|
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"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:51:01 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
# Output: LINES COLUMNS
|
|
|
|
|
$ get_term_size
|
|
|
|
|
15 55
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Get the terminal size in pixels.
|
2018-06-13 05:17:25 +02:00
|
|
|
|
|
2018-06-15 03:40:50 +02:00
|
|
|
|
**CAVEAT**: This does not work in some terminal emulators.
|
2018-06-13 05:17:25 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-13 05:17:25 +02:00
|
|
|
|
```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]}"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:51:01 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
# Output: WIDTHxHEIGHT
|
|
|
|
|
$ get_window_size
|
|
|
|
|
1200x800
|
|
|
|
|
|
|
|
|
|
# Output (fail):
|
|
|
|
|
$ get_window_size
|
|
|
|
|
x
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Get the current cursor position.
|
2018-06-13 05:17:25 +02:00
|
|
|
|
|
|
|
|
|
This is useful when creating a TUI in pure bash.
|
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-13 05:17:25 +02:00
|
|
|
|
```sh
|
|
|
|
|
get_cursor_pos() {
|
|
|
|
|
# Usage: get_cursor_pos
|
|
|
|
|
IFS='[;' read -p $'\e[6n' -d R -rs _ y x _
|
|
|
|
|
printf '%s\n' "$x $y"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 01:51:01 +02:00
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
# Output: X Y
|
|
|
|
|
$ get_cursor_pos
|
|
|
|
|
1 8
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-16 14:33:29 +02:00
|
|
|
|
# Colors
|
|
|
|
|
|
|
|
|
|
## Convert a hex color to RGB.
|
|
|
|
|
|
|
|
|
|
**Example Function:**
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
hex_to_rgb() {
|
|
|
|
|
# Usage: hex_to_rgb "#FFFFFF"
|
|
|
|
|
((r=16#${1:1:2}))
|
|
|
|
|
((g=16#${1:3:2}))
|
|
|
|
|
((b=16#${1:5:6}))
|
|
|
|
|
|
|
|
|
|
printf '%s\n' "$r $g $b"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ hex_to_rgb "#FFFFFF"
|
|
|
|
|
255 255 255
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Convert an RGB color to hex.
|
|
|
|
|
|
|
|
|
|
**Example Function:**
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
rgb_to_hex() {
|
|
|
|
|
# Usage: rgb_to_hex "r" "g" "b"
|
|
|
|
|
printf '#%02x%02x%02x\n' "$1" "$2" "$3"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**Example Usage:**
|
|
|
|
|
|
|
|
|
|
```shell
|
|
|
|
|
$ rgb_to_hex "255" "255" "255"
|
|
|
|
|
#FFFFFF
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
# Code Golf
|
2018-06-14 00:26:00 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Shorter `for` loop syntax.
|
2018-06-14 00:26:00 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
```shell
|
|
|
|
|
# Tiny C Style.
|
|
|
|
|
for((;i++<10;)){ echo "$i";}
|
2018-06-14 00:26:00 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
# Undocumented method.
|
2018-06-15 01:51:01 +02:00
|
|
|
|
for i in {1..10};{ echo "$i";}
|
2018-06-14 00:26:00 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
# Expansion.
|
|
|
|
|
for i in {1..10}; do echo "$i"; done
|
2018-06-14 00:26:00 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
# C Style.
|
|
|
|
|
for((i=0;i<=10;i++)); do echo "$i"; done
|
2018-06-14 00:26:00 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Shorter infinite loops.
|
2018-06-14 00:28:01 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
```shell
|
|
|
|
|
# Normal method
|
|
|
|
|
while :; do echo hi; done
|
2018-06-14 00:28:01 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
# Shorter
|
|
|
|
|
for((;;)){ echo hi;}
|
2018-06-14 00:28:01 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Shorter function declaration.
|
2018-06-14 00:26:00 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
```shell
|
|
|
|
|
# Normal method
|
|
|
|
|
f(){ echo hi;}
|
2018-06-14 00:26:00 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
# Using a subshell
|
|
|
|
|
f()(echo hi)
|
2018-06-14 00:26:00 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
# Using arithmetic
|
|
|
|
|
# You can use this to assign integer values.
|
|
|
|
|
# Example: f a=1
|
|
|
|
|
# f a++
|
|
|
|
|
f()(($1))
|
2018-06-14 00:26:00 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
# Using tests, loops etc.
|
|
|
|
|
# NOTE: You can also use ‘while’, ‘until’, ‘case’, ‘(())’, ‘[[]]’.
|
|
|
|
|
f()if true; then echo "$1"; fi
|
|
|
|
|
f()for i in "$@"; do echo "$i"; done
|
2018-06-14 00:26:00 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Shorter `if` syntax.
|
2018-06-14 00:45:04 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
```shell
|
|
|
|
|
# One line
|
2018-06-15 21:48:27 +02:00
|
|
|
|
# Note: The 3rd statement may run when the 1st is true
|
2018-06-15 01:37:35 +02:00
|
|
|
|
[[ "$var" == hello ]] && echo hi || echo bye
|
|
|
|
|
[[ "$var" == hello ]] && { echo hi; echo there; } || echo bye
|
2018-06-14 00:45:04 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
# Multi line (no else, single statement)
|
2018-06-15 21:48:27 +02:00
|
|
|
|
# Note: The exit status may not be the same as with an if statement
|
2018-06-15 01:37:35 +02:00
|
|
|
|
[[ "$var" == hello ]] && \
|
|
|
|
|
echo hi
|
2018-06-14 00:45:04 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
# Multi line (no else)
|
|
|
|
|
[[ "$var" == hello ]] && {
|
|
|
|
|
echo hi
|
|
|
|
|
# ...
|
2018-06-14 00:45:04 +02:00
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Simpler `case` statement to set variable.
|
2018-06-14 00:51:08 +02:00
|
|
|
|
|
|
|
|
|
We can use the `:` builtin to avoid repeating `variable=` in a case
|
|
|
|
|
statement. The `$_` variable stores the last argument of the last
|
|
|
|
|
successful command. `:` always succeeds so we can abuse it to store the
|
|
|
|
|
variable value.
|
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
```shell
|
|
|
|
|
# Example snippet from Neofetch.
|
|
|
|
|
case "$(uname)" in
|
|
|
|
|
"Linux" | "GNU"*)
|
|
|
|
|
: "Linux"
|
|
|
|
|
;;
|
2018-06-14 00:51:08 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
*"BSD" | "DragonFly" | "Bitrig")
|
|
|
|
|
: "BSD"
|
|
|
|
|
;;
|
2018-06-14 00:51:08 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
"CYGWIN"* | "MSYS"* | "MINGW"*)
|
|
|
|
|
: "Windows"
|
|
|
|
|
;;
|
2018-06-14 00:51:08 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
*)
|
|
|
|
|
printf '%s\n' "Unknown OS detected, aborting..." >&2
|
|
|
|
|
exit 1
|
|
|
|
|
;;
|
|
|
|
|
esac
|
2018-06-14 00:51:08 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
# Finally, set the variable.
|
|
|
|
|
os="$_"
|
2018-06-14 00:51:08 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
# Other
|
2018-06-14 07:12:45 +02:00
|
|
|
|
|
2018-06-15 09:24:12 +02:00
|
|
|
|
## Use `read` as an alternative to the `sleep` command.
|
|
|
|
|
|
|
|
|
|
I was surprised to find out `sleep` is an external command and isn't a
|
|
|
|
|
built-in.
|
|
|
|
|
|
|
|
|
|
**Example Funcrion:**
|
|
|
|
|
|
|
|
|
|
```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
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 08:10:23 +02:00
|
|
|
|
## Check if a program is in the user's PATH.
|
|
|
|
|
|
|
|
|
|
```shell
|
2018-06-15 08:14:08 +02:00
|
|
|
|
# There are 3 ways to do this and you can use either of
|
|
|
|
|
# these in the same way.
|
2018-06-15 08:10:23 +02:00
|
|
|
|
type -p executable_name &>/dev/null
|
2018-06-15 08:14:08 +02:00
|
|
|
|
hash executable_name &>/dev/null
|
|
|
|
|
command -v executable_name &>/dev/null
|
2018-06-15 08:10:23 +02:00
|
|
|
|
|
|
|
|
|
# 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 isn't installed).
|
|
|
|
|
if ! type -p convert &>/dev/null; then
|
|
|
|
|
printf '%s\n' "error: convert isn't installed, exiting..."
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Get the current date using `strftime`.
|
2018-06-14 07:12:45 +02:00
|
|
|
|
|
|
|
|
|
Bash’s `printf` has a built-in method of getting the date which we can use
|
|
|
|
|
in place of the `date` command in a lot of cases.
|
|
|
|
|
|
2018-06-15 03:40:50 +02:00
|
|
|
|
**CAVEAT:** Requires `bash` 4+
|
2018-06-14 07:12:45 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
**Example Function:**
|
|
|
|
|
|
2018-06-14 07:12:45 +02:00
|
|
|
|
```sh
|
|
|
|
|
date() {
|
|
|
|
|
# Usage: date "format"
|
|
|
|
|
# See: 'man strftime' for format.
|
2018-06-14 09:58:54 +02:00
|
|
|
|
printf "%($1)T\\n" "-1"
|
2018-06-14 07:12:45 +02:00
|
|
|
|
}
|
2018-06-15 02:01:56 +02:00
|
|
|
|
```
|
2018-06-14 07:12:45 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
**Example Usage:**
|
2018-06-14 07:12:45 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
```shell
|
|
|
|
|
# Using above function.
|
|
|
|
|
$ date "%a %d %b - %l:%M %p"
|
|
|
|
|
Fri 15 Jun - 10:00 AM
|
2018-06-14 07:12:45 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
# Using printf directly.
|
|
|
|
|
$ printf '%(%a %d %b - %l:%M %p)T\n' "-1"
|
|
|
|
|
Fri 15 Jun - 10:00 AM
|
2018-06-14 07:12:45 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
# 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
|
2018-06-14 07:12:45 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 10:33:01 +02:00
|
|
|
|
## 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'
|
|
|
|
|
```
|
|
|
|
|
|
2018-06-18 01:25:38 +02:00
|
|
|
|
## Get the list of functions from your script.
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
get_functions() {
|
|
|
|
|
# Usage: get_functions
|
|
|
|
|
IFS=$'\n' read -d "" -ra functions < <(declare -F)
|
|
|
|
|
printf '%s\n' "${functions[@]//declare -f }"
|
|
|
|
|
}
|
|
|
|
|
```
|
2018-06-15 10:33:01 +02:00
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Bypass shell aliases.
|
2018-06-14 07:12:45 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
```shell
|
2018-06-14 07:12:45 +02:00
|
|
|
|
# alias
|
2018-06-15 01:37:35 +02:00
|
|
|
|
ls
|
2018-06-14 07:12:45 +02:00
|
|
|
|
|
|
|
|
|
# command
|
2018-06-14 10:07:28 +02:00
|
|
|
|
# shellcheck disable=SC1001
|
2018-06-15 01:37:35 +02:00
|
|
|
|
\ls
|
2018-06-14 07:12:45 +02:00
|
|
|
|
```
|
|
|
|
|
|
2018-06-15 02:01:56 +02:00
|
|
|
|
## Bypass shell functions.
|
2018-06-14 07:12:45 +02:00
|
|
|
|
|
2018-06-15 01:37:35 +02:00
|
|
|
|
```shell
|
2018-06-14 07:12:45 +02:00
|
|
|
|
# function
|
2018-06-15 01:37:35 +02:00
|
|
|
|
ls
|
2018-06-14 07:12:45 +02:00
|
|
|
|
|
|
|
|
|
# command
|
2018-06-15 01:37:35 +02:00
|
|
|
|
command ls
|
2018-06-14 07:12:45 +02:00
|
|
|
|
```
|