Array Manipulation

elements.bsh

Set of -euE safe functions to make bash array manipulation easy

Set of functions for deleting, prepending and appending without repetition. Versions using actual Bash arrays are suffixed with an _a, else it works on delimiter (IFS) separated string

These functions use variable indirection when possible to make using them friendlier.

Note

Not intended for POSIX sh, works on bash and others like ksh

dynamic_set_a
Arguments:
  • $1 - Name of array to be set

  • [$2...] - Values to set

Dynamically set an array to values from arguments

Example

dynamic_set_a my_array_name 11 "2 2" 33
# Is like executing
# my_array_name=(11 "2 2" 33)
dynamic_set
Arguments:
  • $1 - Name of variable to be set

  • $2 - Value to set

Dynamically set a variable to a value. There are a number of properties that need to be preserved when setting a variable, export, local, global, etc… This method will allow you to indirectly set a variable while preserving these properties.

Example

dynamic_set my_name '11 "2 2" 33'
# Is like executing
# my_array_name='11 "2 2" 33'
clear_a
Arguments:

$1 - Name of array to be cleared

Output:

Returns 1 on invalid variable name

Slightly safer indirect clear array

Note

Still uses eval on older versions of bash, but makes sure the variable name appears valid

remove_element_a
Arguments:
  • $1 - Name of array to be set

  • $2 - Value to be removed

Removes all instances of a value from an array

Example

x=(11 22 33 22 44)
remove_element_a x 22
# declare -a x='([0]="11" [1]="33" [2]="44")'

Note

The resulting array is sequentially indexed, which is not typical in bash when removing elements from an array

remove_element
Arguments:
  • $1 - Name of string to be set

  • $2 - String to be removed

Parameters:

[IFS] - IFS Separator used

String version of remove_element_a

add_elements_a
Arguments:
  • $1 - Name of array to be set

  • $2… - Values to be appended

Just appends to array, allows repeats. ${1}+=("${@}")

add_element_post_a
Arguments:
  • $1 - Name of array to be set

  • $2 - Value to be appended

Add a value to the end of an array

Removes all copies of the value from the array first, and then appends to the end of the array

Example

x=(22 11 22 33)
add_element_post_a x 22
declare -p x
# declare -a x='([0]="11" [1]="33" [2]="22")'

Note

The resulting array is sequentially indexed

add_element_post
Arguments:
  • $1 - Name of string to be set

  • $2 - String to be appended

Parameters:

[IFS] - IFS Separator used

String version of add_element_post_a

add_element_pre_a
Arguments:
  • $1 - Name of array to be set

  • $2 - Value to be prepended

Add a value to the beginning of an array

Removes all copies of the value from the array first, and then prepends to the beginning of the array

Example

x=(11 22 33 22)
add_element_pre_a x 22
declare -p x
# declare -a x='([0]="22" [1]="11" [2]="33")'

Note

The resulting array is sequentially indexed

add_element_pre
Arguments:
  • $1 - Name of string to be set

  • $2 - String to be prepended

Parameters:

[IFS] - IFS Separator used

String version of add_element_post_a

cmp_elements_a
Arguments:
  • $1 - First array name

  • $2 - Second array name

Output:

Return Value: 0 - Identical, 1 - Not identical

Checks if two arrays are the same

cmp_elements
Arguments:
  • $1 - First string name

  • $2 - Second string name

Output:

Return Value: 0 - Identical, 1 - Not identical

String version of cmp_elements_a

split_s
Arguments:
  • $1 - Target array name

  • $2… - Strings

Parameters:
  • [MIFS] - A Multicharacter Internal Field Separator.

  • Default: //

Splits an string into an array, using an MIFS

Author

https://stackoverflow.com/a/47633817/4166604

join_a
Arguments:
  • $1 - Target string name

  • $2… - Array elements

Parameters:
  • [MIFS] - A Multicharacter Internal Field Separator.

  • Default: //

Return Value:
  • 0 - Success

  • 1 - No elements were provided. This is done to create a distinction between join_a x and join_a x "".

Joins an array into a string, using an MIFS

Note

When 1 is returned, this is the only time the output is not compatible with split_s.

join_a_out
Arguments:
  • $1 - Hex code for IFS

  • $2… - Strings

Parameters:
  • [MIFS] - A Multicharacter Internal Field Separator, using awk escape format. (Except for null, which is a double backslash \x00).

  • Default: //

  • Note: If you are using “” or $’’ notation, you will need “\\x00”

Instead of storing the value to a variable, this outputs to stdout. Unlike join_a, join_a_out can handle special characters, like null. Because macOS awk is so difficult, instead of ‘x00’ for null, use ‘\x00’. No other form of null will work on macOS. All other sed escape character should work.

Author

https://superuser.com/a/720157/352118

is_array
Arguments:
  • $1 - variable name

Output:

Returns 0 if variable is an array, else 1

Determines if $1 is an array or not. If it is not defined, still returns 1

is_associative_array
Arguments:
  • $1 - variable name

Output:

Returns 0 if variable is an associative array, else 1

Determines if $1 is an associative array or not. If it is not defined, still returns 1

to_array
Arguments:
  • $1 - variable name

Convert an string to an array, honoring quotes and newlines

$ x=$'11 "2\n2" "3 \t 3 " ""'
$ to_array x
$ declare -p x
declare -a x=([0]="11" [1]=$'2\n2' [2]=$'3 \t 3' [3]="")

Bugs

$ x='"f\o\\o" f\o\\o '\''f\o\\o'\'
$ eval "y=(${x})"
$ to_array x
$ declare -p x
$ declare -p y
declare -a x=([0]="f\\o\\\\o" [1]="fo\\o" [2]="f\\o\\\\o")
declare -a y=([0]="f\\o\\o" [1]="fo\\o" [2]="f\\o\\\\o")
# Only the double backslash in double quotes comes out wrong, due to a bug in xargs? To get around this bug, use ``eval`` instead
array_length
Arguments:
  • $1 - Array name

Output:

stdout - number of elements in the array. -1 for variable not defined.

Echoes out the length of the array. If the variable is not defined, echos -1.

Note

Does not differentiate between an array of length 1 and a non-array variable. Use is_array for that

subtract_array
Arguments:
  • $1 - Array 1

  • $2 - Array 2

  • $3 - Destination Array

Destination array becomes values of array 2 not in array 1 (1 - 2). The destination array name can be the same as Array 1 or 2.

array_to_variable
Arguments:
  • $1 - Array name

  • $2 - Variable name prefix

Converts an array to a set of variables, starting with the prefix ${2}_ numbered from 1 to N in sequential order, even if there are holes in the array.

variables_to_array
Arguments:
  • $1 - Variable name prefix

  • $2 - Array name

Converts a set of variables to an array, starting with the prefix ${1}_ numbered from 1 to N in sequential order, stops as soon as one does not exist.

Note

Will not detect variables that are declared but unassigned, will work with variables set to null.

all_variables_to_array

Same as variables_to_array, but can skip indexing numbers

See also

variables_to_array

Sequential only version of function

serialize_array

Serializes an array (or associative array in bash 4 or newer) into a single string

Arguments:

$1 - Name of array

Output:

stdout - The serialized string representing the array

Handles any array, all special characters without issue, for both key and value, except NULL which isn’t allowed in any bash variable anyways. However, the results will not differentiate between an unassigned array and an empty array.

An array is converted to a string using the following schema

Array
"a" + (index0 + " " + length_of_value0 + " " + value0) + (index1 + ...
Associative Array
"A" + (length_of_key0 + " " + key0 + length_of_value0 + " " + value0) + ...

Example

x=(11 22 33)
unset x[1]
y="$(serialize_array x)"
# "a0 2 112 2 33"

deserialize_array x2 "${y}"
# x and x2 are identical
deserialize_array

Convert a serialized array (string) into an array or associative array.

Arguments:
  • $1 - Output variable name

  • $2 - Serialized string

If you do not declare the output variable before calling deserialize_array, it will attempt to create it. This works pretty well for indexed arrays, but associative arrays are trickier. On bash 4.2 or newer, it will create it as a global variable if it is not already defined as an associative array. This makes controlling the scope difficult and does not work in older versions of bash. It’s best to define the variable before calling, if possible.

See also

serialize_array