Unix shell script kit is a collection of utility functions and constants.
The kit works with POSIX shells, including bash, zsh, dash, ksh, sh, etc.
All suggestions are welcome and appreciated.
Download the kit as one file that has everything:
curl -O "https://raw.githubusercontent.com/SixArm/unix-shell-script-kit/main/unix-shell-script-kit"
To use the kit in your own script, you source the kit like this:
. /your/path/here/unix-shell-script-kit
To use the kit in your own script in the same directory, you source the kit like this:
. "$(dirname "$(readlink -f "$0")")/unix-shell-script-kit"
- Package: unix-shell-script-kit
- Version: 12.3.0
- Created: 2017-08-22T00:00:00Z
- Updated: 2024-12-10T23:09:11Z
- Website: https://github.com/sixarm/unix-shell-script-kit
- License: GPL-2.0 or GPL-3.0 or contact us for more
- Contact: Joel Parker Henderson ([email protected])
Print output message to stdout.
out "my message"
STDOUT=> my message
Print error message to stderr.
err "my message"
STDERR=> my message
Print error message to stderr, then exit with an error code.
die 1 "my message"
STDERR=> my message
=> exit 1
Example with an exit code number and name:
die $EXIT_UNAVAILABLE EXIT_UNAVAILABLE
STDERR=> EXIT_UNAVAILABLE
=> exit 69
Example with more diagnostics:
false || die $? "Fatal error $? on line $LINENO of $0"
STDERR=> Fatal error 1 on line 2 of example.sh"
=> exit 1
Print a big banner to stdout, good for human readability.
big "my message"
=>
###
#
# my message
#
###
Print a datestamp, unique random id, hostname, process id, and message.
log "my message"
=> 2021-05-04T22:57:54.000000000+00:00 7e7151dc24bd511098ebb248771d8ffb abc.example.com 1234 my message
Generate a 32-bit secure random lowercase hex identifier.
zid
=> 78577554e967951388b5907854b4c337
Prompt the user for a line of input, then return a trimmed string.
ask
=> prompt
Should the program print UTF-8 characters such as accents and emoji?
This implementation is a heuristic and subject to change as we learn more. This checks the locale charmap. However, it's possible for the charmap to be set to UTF-8, but the terminal is unable to render UTF-8 characters.
utf8
=> true
Print a success message to stdout, with customization via env vars.
print_success "This is a success message."
=> This is a success message.
The output can be customized by setting:
- PRINT_SUCCESS_START
- PRINT_SUCCESS_STOP
Print a warning message to stdout.
print_warning "This is a warning message."
=> This is a warning message.
The output can be customized by setting:
- PRINT_WARNING_START
- PRINT_WARNING_STOP
Print a failure message to stdout, with customization via env vars.
print_failure "This is a failure message."
=> This is a failure message.
The output can be customized by setting:
- PRINT_FAILURE_START
- PRINT_FAILURE_STOP
Get a datetime using our preferred ISO format.
now
=> 2021-05-04T22:59:28.769653000+00:00
Example with a custom datetime:
now -d "2021-01-01"
=> 2021-01-01T00:00:00.000000000+00:00
Get a date using our preferred ISO format.
now_date
=> 2021-05-04
Example with a custom date, if your date command offers option -d:
now_date -d "January 1, 2021"
=> 2021-01-01
Get the current time in POSIX seconds.
sec
=> 1620169178
Get the age of a given time in POSIX seconds.
age 1620169178
=> 19
Is the age of a given time newer than a given number of seconds?
newer 2000000000 && echo "true" || echo "false
=> true
Is the age of a given time older than a given number of seconds?
older 1000000000 && echo "true" || echo "false"
=> true
Does a directory exist?
directory_exists /usr
=> true
directory_exists /loremipsum
=> false
Ensure a directory exists.
directory_exists_or_die /usr
=> true
directory_exists_or_die /loremipsum
STDERR=> Directory needed: /loremipsum
=> exit $EXIT_IOERR
Does a file exist?
file_exists foo.txt
=> true
file_exists loremipsum.txt
=> false
Ensure a file exists.
file_exists_or_die foo.txt
=> true
file_exists_or_die loremipsum.txt
STDERR=> File needed: loremipsum.txt
=> exit $EXIT_IOERR
Does a symlink exist?
symlink_exists foo.txt
=> true
symlink_exists loremipsum.txt
=> false
Ensure a symlink exists.
symlink_exists_or_die foo.txt
=> true
symlink_exists_or_die loremipsum.txt
STDERR=> Symlink needed: loremipsum.txt
=> exit $EXIT_IOERR
Does a command exist?
command_exists grep
=> true
command_exists curl
=> false
Ensure a command exists, otherwise die with a help message.
command_exists_or_die grep
=> true
command_exists_or_die loremipsum
STDERR=> Command needed: loremipsum
=> exit 1
Ensure a command exists and version is sufficient, otherwise die with a help message.
command_version_exists_or_die grep 1.1 2.2
=> true
command_version_exists_or_die grep 3.3 2.2
STDERR=> Command version needed: grep >= 3.3 (not 2.2)
=> exit 1
Does a variable exist?
var_exists HOME
=> true
var_exists FOO
=> false
Ensure a variable exists, otherwise die with a help message.
var_exists_or_die HOME
=> true
var_exists_or_die FOO
STDERR=> Variable needed: FOO
=> exit 1
Is a version sufficient?
version 1.1 2.2
=> true
version 3.3 2.2
=> false
Ensure a version is sufficient, otherwise die with a help message.
version_or_die 1.1 2.2
=> true
version_or_die 3.3 2.2
STDERR=> Version needed: >= 3.3 (not 2.2)
Convert a number string to an integer number string.
int 1.23
=> 1
Print the sum of numbers.
sum 1 2 3
=> 6
Compare alnums as groups, such as for word version strings.
cmp_alnums "a.b.c" "a.b.d"
# => -1 (negative one means left < right)
Compare digits as groups, such as for number version strings.
cmp_digits "1.2.3" "1.2.4"
# => -1 (negative one means left < right)
Source all the executable files in a given directory and subdirectories.
dot_all ~/temp
=> . ~/temp/a.sh
=> . ~/temp/b.pl
=> . ~/temp/c.js
Run all the executable commands in a given directory and subdirectories.
run_all ~/temp
=> ~/temp/a.sh
=> ~/temp/b.pl
=> ~/temp/c.js
Shell all the executable commands in a given directory and subdirectories.
sh_all ~/temp
=> sh -c ~/temp/a.sh
=> sh -c ~/temp/b.pl
=> sh -c ~/temp/c.js
Remove all files in a given directory and subdirectories-- use with caution.
rm_all ~/temp
=> rm ~/temp/a.sh
=> rm ~/temp/b.pl
=> rm ~/temp/c.js
Remove any space characters at the text's start or finish.
trim " foo "
=> foo
Convert a string from any characters to solely lowercase and single internal dash characters.
slug "**Foo** **Goo** **Hoo**"
=> foo-goo-hoo
Convert a string from any characters to solely lowercase and single internal dash characters and slash characters.
slugs "**Foo** / **Goo** / **Hoo**"
=> foo/goo/hoo
Convert text from any lowercase letters to uppercase letters.
upper_format AbCdEf
=> ABCDEF
Convert text from any uppercase letters to lowercase letters.
lower_format AbCdEf
=> abcdef
Convert a string from any characters to solely alphanumeric and single internal dash characters.
chain_format "**Foo** **Goo** **Hoo**"
=> Foo-Goo-Hoo
Convert a string from any characters to solely alphanumeric and single internal underscore characters.
snake_format "**Foo** **Goo** **Hoo**"
=> Foo_Goo_Hoo
Convert a string from any characters to solely alphanumeric and single internal space characters.
space_format "**Foo** **Goo** **Hoo**"
=> Foo Goo Hoo
Convert a string from any characters to solely a command "touch -t" timestamp format.
touch_format "Foo 2021-05-04 22:57:54 Goo"
=> 202105042257.54
Get a string's characters that match a class, with optional offset and length.
Syntax:
select_character_class <string> <class> [offset [length]]
Example with character class:
select_character_class foo123goo456 alpha
=> foogoo
Example with character class and substring offset:
select_character_class foo123goo456 alpha 3
=> goo
Example with character class and substring offset and length:
select_character_class foo123goo456 alpha 3 1
=> g
Get a string's characters that don't match a class, with optional offset and length.
Syntax:
reject_character_class <string> <class> [offset [length]]
Exapmle with character class:
reject_character_class foo123goo456 alpha
=> 123456
Example with character class and substring offset:
reject_character_class foo123goo456 alpha 3
=> 456
Example with character class and substring offset and length:
reject_character_class foo123goo456 alpha 3 1
=> 4
Generate random characters
Syntax:
random_char [characters [length]]
Example:
random_char ABCDEF 8
=> CBACBFDD
Example hexadecimal digit uppercase:
random_char 0-9A-F 8
=> FC56A95C
Example character class for uppercase letters:
random_char '[:upper:]' 8
=> ZMGIQBJB
POSiX character classes for ASCII characters:
Class Pattern Description
---------- ------------- -----------
[:upper:] [A-Z] uppercase letters
[:lower:] [a-z] lowercase letters
[:alpha:] [A-Za-z] uppercase letters and lowercase letters
[:alnum:] [A-Za-z0-9] uppercase letters and lowercase letters and digits
[:digit:] [0-9] digits
[:xdigit:] [0-9A-Fa-f] hexadecimal digits
[:punct:] punctuation (all graphic characters except letters and digits)
[:blank:] [ \t] space and TAB characters only
[:space:] [ \t\n\r\f\v] whitespace characters (space, tab, newline, return, feed, vtab)
[:cntrl:] control characters
[:graph:] [^ [:cntrl:]] graphic characters (all characters which have graphic representation)
[:print:] [[:graph:] ] graphic characters and space
Get random characters using [:alnum:]
class.
random_char_alnum 8
=> 1Yp7M7wc
Get random characters using [:alpha:]
class.
random_char_alpha 8
=> dDSmQlYD
Get random characters using [:blank:]
class.
random_char_blank 8
=> " \t \t \t"
Get random characters using [:cntrl:]
class.
random_char_cntrl 8
=> "^c^m^r^z^a^e^p^u"
Get random characters using [:digit:]
class.
random_char_digit 8
=> 36415110
Get random characters using [:graph:]
class.
random_char_graph 8
=> e'2-3d+9
random_char_lower 8
=> pgfqrefo
Get random characters using [:lower:][:digit]
classes
random_char_lower_digit 8
=> 69m7o83i
Get random characters using [:upper:]
class.
random_char_upper 8
=> EGXUHNIM
Get random characters using [:upper:][:digit:]
classes
random_char_upper_digit 8
=> L2PT37H6
Get random characters using [:print:]
class.
random_char_print 8
=> ),zN87K;
Get random characters using [:space:]
class.
random_char_space 8
=> "\n \t\r \v \f"
Get random characters using [:xdigit:]
class.
random_char_xdigit 8
=> eC3Ce9eD
Get the array number of fields a.k.a. length a.k.a. size.
set -- a b c d
array_n "$@"
=> 4
Get the array item at index i
which is 1-based.
set -- a b c d
array_i "$@" 3
=> c
Get the array's first item.
set -- a b c d
array_first "$@"
=> a
Get the array's last item.
set -- a b c d
array_last "$@"
=> d
Get the array's car item a.k.a. first item.
set -- a b c d
array_car "$@"
=> a
Get the array's cdr items a.k.a. everything after the first item.
set -- a b c d
array_car "$@"
=> b c d
Colors use ANSI color codes and corresponding variable names:
printf "%sblack\n" $COLOR_BLACK
printf "%sred\n" $COLOR_RED
printf "%sgreen\n" $COLOR_GREEN
printf "%syellow\n" $COLOR_YELLOW
printf "%sblue\n" $COLOR_BLUE
printf "%smagenta\n" $COLOR_MAGENTA
printf "%scyan\n" $COLOR_CYAN
printf "%swhite\n" $COLOR_WHITE
printf "%reset\n" $COLOR_RESET.
The kit will use these colors for standard output and standard error:
STDOUT_COLOR_START="$COLOR_BLUE"
STDOUT_COLOR_STOP="$COLOR_RESET"
STDERR_COLOR_START="$COLOR_RED"
STDERR_COLOR_STOP="$COLOR_RESET"
Should the program use color?
The color logic heuristic in order of priority:
- If NO_COLOR is set to non-empty, then no.
- If CLICOLOR_FORCE is set to non-empty, then yes.
- If the TERM is set to "dumb", then no.
- If the output is on a terminal, then yes.
- Otherwise, no.
Assert a test utility command succeeds.
assert_test -x program.sh
=> success i.e. no output
assert_test -x notes.txt
STDERR=> assert_test -x notes.txt
Assert an item is empty i.e. null.
assert_empty ""
=> success i.e. no output
assert_empty foo
STDERR=> assert_empty foo
Assert an item is not empty i.e. not null.
assert_not_empty foo
=> success i.e. no output
assert_not_empty ""
STDERR=> assert_not_empty
Assert an integer comparison.
assert_int_eq 1 1
=> success i.e. no output
assert_int_eq 1 2
STDERR=> assert_int_eq 1 2
There are comparison assertions for integers:
assert_int_eq
is equal toassert_int_ne
is not equal toassert_int_ge
is greater than or equal toassert_int_gt
is greater thanassert_int_le
is less than or equal toassert_int_lt
is less than
Assert a string comparison.
assert_str_eq foo foo
=> success i.e. no output
assert_str_eq foo bar
STDERR=> assert_str_eq foo bar
There are comparison assertions for strings:
assert_str_eq
is equal toassert_str_ne
is not equal toassert_str_ge
is greater than or equal toassert_str_gt
is greater thanassert_str_le
is less than or equal toassert_str_lt
is less than
Assert a string starts with a substring.
assert_str_starts_with foobar foo
=> success i.e. no output
assert_str_starts_with foobar xxx
STDERR=> assert_str_starts_with foobar xxx
Assert a string ends with with a substring.
assert_str_ends_with foobar foo
=> success i.e. no output
assert_str_ends_with foobar xxx
STDERR=> assert_str_ends_with foobar xxx
Make a temporary directory path.
mktemp_dir
=> /var/folders/4f7b65122b0fb65b0fdad568a65dc97d
Make a temporary file path.
mktemp_file
=> /var/folders/4f7b65122b0fb65b0fdad568a65dc97d/1d9aafac5373be95d8b4c2dece0b1197
Get a file's media type a.k.a. mime type such as "text/plain".
file_media_type notes.txt
=> text/plain
Get a file's media type type a.k.a. mime type such as "text".
file_media_type_supertype notes.txt
=> text
Get a file's media type subtype a.k.a. mime type such as "plain".
file_media_type_subtype notes.txt
=> plain
Does a font name exist on this system?
font_name_exists Arial
=> true
font_name_exists Foo
=> false
Ensure a font name exists, otherwise die with a help message.
font_name_exists_or_die Arial
=> true
font_name_exists_or_die Foo
STDERR=> Font needed: Foo
=> exit 1
Does a file end with a newline?
file_ends_with_newline notes.txt
=> true
Get a user-specific directory via env var, or XDG setting, or HOME.
user_dir foo
=> $FOO_DIR || $FOO_HOME || $XDG_FOO_DIR || $XDG_FOO_HOME || $HOME/foo
Conventions:
user_dir bin
=> binary executable directoryuser_dir cache
=> cache directoryuser_dir config
=> configuration directoryuser_dir data
=> data directoryuser_dir desktop
=> desktop directoryuser_dir documents
=> documents directoryuser_dir download
=> download directoryuser_dir log
=> logging directoryuser_dir music
=> music directoryuser_dir pictures
=> pictures directoryuser_dir publicshare
=> public share directoryuser_dir runtime
=> runtime directoryuser_dir state
=> state directoryuser_dir temp
=> temporary directoryuser_dir templates
=> templates directoryuser_dir videos
=> videos directory
Popular XDG conventions:
XDG_DESKTOP_DIR
=> user-specific desktop, such as frequent apps and files.XDG_DOCUMENTS_DIR
=> user-specific documents, such as typical working files.XDG_DOWNLOAD_DIR
=> user-specific downloads, such as internet file downloads.XDG_MUSIC_DIR
=> user-specific music files, such as songs.XDG_PICTURES_DIR
=> user-specific pictures, such as photos.XDG_PUBLICSHARE_DIR
=> user-specific public share, such as file sharing.XDG_TEMPLATES_DIR
=> user-specific templates.XDG_VIDEOS_DIR
=> user-specific videos, such as movies.
POSIX XDG conventions:
XDG_BIN_HOME
=> user-specific binaries, analogous to system /usr/bin or $HOME/.local/bin.XDG_LOG_HOME
=> user-specific log files, analogous to system /var/log or $HOME/.local/log.XDG_TEMP_HOME
=> user-specific temporary files, analogous to system /temp or $HOME/.temp.XDG_DATA_HOME
=> user-specific data files, analogous to system /usr/share or $HOME/.local/share.XDG_CACHE_HOME
=> user-specific cache files, analogous to system /var/cache or $HOME/.cache.XDG_STATE_HOME
=> user-specific cache files, analogous to system /var/state or $HOME/.local/state.XDG_CONFIG_HOME
=> user-specific configuration files, analogous to system /etc or $HOME/.config.XDG_RUNTIME_HOME
=> user-specific runtime files such as sockets, named pipes, etc. or $HOME/.runtime.