Get the length of an empty or unset array when the "nounset" option is valid

Due to the fact that Bash, when operating in set -o nounset (aka set -u ) mode, can consider empty arrays as unset, regardless of whether they were really assigned an empty value, you should be careful when trying to expand the array - one of the workarounds is to check if the length of the array is equal to zero. Not to mention the fact that getting the number of elements in an array is a common operation in itself.

When developing with Bash 4.2.47 (1) -release in openSUSE 42.1, I got used to the fact that getting the size of the array using ${#ARRAY_NAME[@]} succeeds when the array is either empty or not defined. However, checking my script with Bash 4.3.46 (1) -release in FreeBSD 10.3, it turned out that this operation might fail with the general error message "unbound variable". Providing a default value for the extension does not seem to work for the length of the array. Providing alternative command chains seems to work, but not inside the function caused by the extension of the subnets - the functions just exit after the first failure. What else can help?

Consider the following example:

 function Size () { declare VAR="$1" declare REF="\${#${VAR}[@]}" eval "echo \"${REF}\" || echo 0" 2>/dev/null || echo 0 } set -u declare -a MYARRAY echo "size: ${#MYARRAY[@]}" echo "size: ${#MYARRAY[@]-0}" echo "Size: $(Size 'MYARRAY')" echo -n "Size: "; Size 'MYARRAY' 

In an openSUSE environment, all echo lines echo 0 , as expected. In FreeBSD, the same result is possible only when the array is explicitly assigned an empty value: MYARRAY=() ; otherwise, both inline queries in the first two lines fail, the third line simply displays Size: (which means that the result of the extension is empty), and only the last line completes completely thanks to the external || echo 0 || echo 0 , however, passing the result through to the screen is not what is usually assumed when trying to get the length of the array.

Here is a summary of my observations:

  Bash 4.2 Bash 4.3 openSUSE FreeBSD counting elements of unset array OK FAILED counting elements of empty array OK OK content expansion of unset array FAILED FAILED content expansion of unset array(*) OK OK content expansion of empty array FAILED FAILED content expansion of empty array(*) OK OK (* with fallback value supplied) 

For me it looks rather inconsistent. Is there any real future and cross-platform solution for this?

+5
source share
2 answers

As a workaround, I followed the route suggested by @ william-threll and simply turned off the nounset option during the request:

 function GetArrayLength () { declare ARRAY_NAME="$1" declare INDIRECT_REFERENCE="\${#${ARRAY_NAME}[@]}" case "$-" in *'u'*) set +u eval "echo \"${INDIRECT_REFERENCE}\"" set -u ;; *) eval "echo \"${INDIRECT_REFERENCE}\"" ;; esac } 

(Using if instead of case leads to negligibly slow execution on my test machines. Moreover, case makes it easy to easily execute additional parameters if it is ever needed.)

I also tried to use the fact that content expansion (with backup or restore value) usually succeeds even for uninstalled arrays:

 function GetArrayLength () { declare ARRAY_NAME="$1" declare INDIRECT_REFERENCE="${ARRAY_NAME}[@]" if [[ -z "${!INDIRECT_REFERENCE+isset}" ]]; then echo 0 else INDIRECT_REFERENCE="\${#${ARRAY_NAME}[@]}" eval "echo \"${INDIRECT_REFERENCE}\"" fi } 

However, it turns out that Bash does not optimize the extension ${a[@]+b} , since the execution time is clearly increased for large arrays - although it is the smallest for empty or unset arrays.

However, if someone has a better solution, he was unable to post other answers.

+1
source

There are known (documented) differences between Linux versions and BSD bash . I would suggest writing your code in accordance with the POSIX standard. You can start here for more information → www2.opengroup.org.

With that in mind, you can run bash using the set -o posix command-line --posix or run the set -o posix --posix command while bash is running. Or it will cause bash to conform to the POSIX standard.

The proposal above will increase the likelihood of cross-platform consistency.

+1
source

All Articles