1

After seeing this question, I decided to put together a function to remove array empty elements in case it saves someone a few seconds.

Is there any way to return (or export) a dynamically named input array-variable as the output of a function?

Ideally

  1. User calls: removeArrayBlanks "newArrayName" "arrayItem1" "" "arrayItem2"...
  2. The function unsets the old array and creates: ${newArrayName[@]}, which expands to "arrayItem1" "arrayItem2" without any blank items or non-sequential index numbers

Also, does anyone have any optimizations/suggestions to the function? I've included the function below. Thanks!

removeArrayBlanks() {
    # Usage: Provide array as input, store output as array. 
    # Example 1: mapfile -t lastArray < <(removeArrayBlanks "hate" "" "empty" "array" "items")
    # Example 2: mapfile -t lastArray < <(removeArrayBlanks "${inputArray[@]}")
    [[ $# -lt 2 ]] && echo "Usage: Provide array as an argument." && exit 1 # Ensure input is array
    tempArray=( "$@" ) # Rebuild array from inputs
    for i in "${!tempArray[@]}"; do
        [[ ! -z "${tempArray[$i]}" ]] && finalArray+=( "${tempArray[$i]}" ) # Add non-empty strings
    done && printf '%s\n' "${finalArray[@]}" && unset tempArray && unset finalArray
}

7
  • Unless you declare it local, variables assigned in a function are visible in the main script, so you don't need to return it. Commented Aug 2, 2021 at 20:58
  • do you want to remove the 'blanks' from the original array, or create a brand new array with no 'blanks'? Commented Aug 2, 2021 at 21:01
  • ! -z seems odd. Commented Aug 2, 2021 at 21:03
  • @Barmar ty, was have a hard time getting the new array to be called $1 from user input. I couldn't find an easy way to 'update' the input array, without forcing the user to have to call a new array name. Commented Aug 2, 2021 at 21:06
  • @markp-fus trying to just remove blanks from input array. And, ideally, not have to rename it or declare a new array. Commented Aug 2, 2021 at 21:07

1 Answer 1

2

Assumptions:

  • objective is to remove array elements that contain empty strings (aka 'blanks') from current array
  • function should be able to work for any array (name) passed to the function
  • OP is running, or can upgrade to/above, bash 4.3+ (needed for nameref support)

NOTE: to 'remove' an array element just unset the array reference (eg, unset array[index#1]

One idea using a nameref in the function and the unset command:

function removeArrayBlanks() {

declare -n localarray="${1}"                                  # define nameref; remains a local array variable

for i in "${!localarray[@]}"                                  # loop through indices
do
    [[ -z "${localarray[${i}]}" ]] && unset localarray[${i}]  # remove entries that contain empty strings
done
}

In operation:

$ myarray=( a "" 1 "" A )
$ typeset -p myarray
declare -a myarray=([0]="a" [1]="" [2]="1" [3]="" [4]="A")

$ removeArrayBlanks myarray
$ typeset -p myarray
declare -a myarray=([0]="a" [2]="1" [4]="A")

# verify localarray not known by parent
$ typeset -p localarray
-bash: typeset: localarray: not found
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks @markp-fuso! I didn't realize that the variable would be updated in the current context/shell from which the function was called. I figured I would have had to 'export' the new array somehow.
Ahh, I see...I suppose I wasn't wrong? I just didn't realized that this was possible: declare -n "Give each name the nameref attribute, making it a name reference to another variable. That other variable is defined by the value of name. All references, assignments, and attribute modifications to name, except those using or changing the -n attribute itself, are performed on the variable referenced by name's value."
keep in mind a function call is (in essence) a macro call (think replace '<function-name>' with '<function_body>') and not a sub-process call; net result is anything you do in the function is actually applied in the 'parent' process; one exception is if you define a variable as 'local' to the function in which case the 'parent' does not see it; in this case the `nameref' is a 'local' variable that also doubles as a reference (and not a copy) of the passed in array name
I opted to populate the array first and then remove/unset the empty strings; you could still implement your logic ... filter out the empty strings before assiging values to the array ... via a function, but also use a nameref to eliminate the overhead of creating/printing/trashing 'temporary' arrays ... your choice
Yes, adding in the nameref was one thing I didn't realize. The other thing I am seeing that I missed was not expanding out the array as function argument. That is, I was doing removeArrayBlanks ${inputArray[@]} which I gather would cause the array name to be lost, because the array is expanded before function can do stuff to it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.