216

I have an array which gets filled with different error messages as my script runs.

I need a way to check if it is empty of not at the end of the script and take a specific action if it is.

I have already tried treating it like a normal VAR and using -z to check it, but that does not seem to work. Is there a way to check if an array is empty or not in Bash?

10 Answers10

266

Supposing your array is $errors, just check to see if the count of elements is zero.

if [ ${#errors[@]} -eq 0 ]; then
    echo "No errors, hooray"
else
    echo "Oops, something went wrong..."
fi
Michael Hampton
  • 252,907
63

I generally use arithmetic expansion in this case:

if (( ${#a[@]} )); then
    echo not empty
fi
x-yuri
  • 2,526
14

You can also consider the array as a simple variable. In that way, just using

if [ -z "$array" ]; then
    echo "Array empty"
else
    echo "Array non empty"
fi

or using the other side

if [ -n "$array" ]; then
    echo "Array non empty"
else
    echo "Array empty"
fi

The problem with that solution is that if an array is declared like this: array=('' foo). These checks will report the array as empty, while it is clearly not. (thanks @musiphil!)

Using [ -z "$array[@]" ] is clearly not a solution neither. Not specifying curly brackets tries to interpret $array as a string ([@] is in that case a simple literal string) and is therefore always reported as false: "is the literal string [@] empty?" Clearly not.

wget
  • 327
9

I checked it with bash-4.4.0:

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]} ]]; then
        echo not empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

and bash-4.1.5:

#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]:+${array[@]}} ]]; then
        echo non-empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

In the latter case you need the following construct:

${array[@]:+${array[@]}}

for it to not fail on empty or unset array. That's if you do set -eu like I usually do. This provides for more strict error checking. From the docs:

-e

Exit immediately if a pipeline (see Pipelines), which may consist of a single simple command (see Simple Commands), a list (see Lists), or a compound command (see Compound Commands) returns a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test in an if statement, part of any command executed in a && or || list except the command following the final && or ||, any command in a pipeline but the last, or if the command’s return status is being inverted with !. If a compound command other than a subshell returns a non-zero status because a command failed while -e was being ignored, the shell does not exit. A trap on ERR, if set, is executed before the shell exits.

This option applies to the shell environment and each subshell environment separately (see Command Execution Environment), and may cause subshells to exit before executing all the commands in the subshell.

If a compound command or shell function executes in a context where -e is being ignored, none of the commands executed within the compound command or function body will be affected by the -e setting, even if -e is set and a command returns a failure status. If a compound command or shell function sets -e while executing in a context where -e is ignored, that setting will not have any effect until the compound command or the command containing the function call completes.

-u

Treat unset variables and parameters other than the special parameters ‘@’ or ‘*’ as an error when performing parameter expansion. An error message will be written to the standard error, and a non-interactive shell will exit.

If you don't need that, feel free to omit :+${array[@]} part.

Also do note, that it's essential to use [[ operator here, with [ you get:

$ cat 1.sh
#!/usr/bin/env bash
set -eu
array=(a b c d)
if [ "${array[@]}" ]; then
    echo non-empty
else
    echo empty
fi

$ ./1.sh _/1.sh: line 4: [: too many arguments empty

x-yuri
  • 2,526
3

If you want to detect an array with empty elements, like arr=("" "") as empty, same as arr=()

You can paste all the elements together and check if the result is zero-length. (Building a flattened copy of the array contents is not ideal for performance if the array could be very large. But hopefully you aren't using bash for programs like that...)

But "${arr[*]}" expands with elements separated by the first character of IFS. So you need to save/restore IFS and do IFS='' to make this work, or else check that the string length == # of array elements - 1. (An array of n elements has n-1 separators). To deal with that off-by-one, it's easiest to pad the concatenation by 1

arr=("" "")

## Assuming default non-empty IFS
## TODO: also check for ${#arr[@]} -eq 0
concat="${arr[*]} "      # n-1 separators + 1 space + array elements
[[ "${#concat}" -ne "${#arr[@]}" ]]  && echo not empty array || echo empty array

test case with set -x

### a non-empty element
$ arr=("" "x")
  + arr=("" "x")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat=' x '
  + [[ 3 -ne 2 ]]
  + echo not empty array
not empty array

### 2 empty elements
$ arr=("" "")
  + arr=("" "")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat='  '
  + [[ 2 -ne 2 ]]
  + echo empty array
empty array

Unfortunately this fails for arr=() : [[ 1 -ne 0 ]]. So you'd need to check for actually empty arrays first separately.


Or with IFS=''. Probably you'd want to save/restore IFS instead of using a subshell, because you can't get a result out of a subshell easily.

# inside a () subshell so we don't modify our own IFS
(IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)

example:

$ arr=("" "")
$ (IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)
   + IFS=
   + [[ -n '' ]]
   + echo empty array
empty array

does work with arr=() - it's still just the empty string.

2

I prefer to use double brackets:

if [[ ! ${array[@]} ]]
then
    echo "Array is empty"
else
    echo "Array is not empty"
fi

Double Brackets: https://stackoverflow.com/questions/669452/is-preferable-over-in-bash

Edited in order to work as expected

bazeusz
  • 105
Nick Tsai
  • 1,358
0

Sadly I cannot comment yet - Regarding @Nick Tsai - I didn't work for me but the following:

if [[ ! ${arrayName[*]} ]]; then ...
if [[ ! ${arrayOne[*]} && ! ${arrayTwo[*]} ]]; then ...

spacing is important for this to work!

Alkeno
  • 1
0
arr=()
#arr=(1 2 3)
if [[ -v arr ]];then
    echo "not empty"
else
    echo "empty"
fi

0

In my case, the second Answer was not enough because there could be whitespaces. I came along with:

if [ "$(echo -ne ${opts} | wc -m)" -eq 0 ]; then
  echo "No options"
else
  echo "Options found"
fi
Micha
  • 111
-1

You could also benefit from jq if you got it installed on your system:

if [[ $(echo $array | jq -r 'length') -eq 0 ]]
then
    echo "Array is empty"
else
    echo "Array is not empty"
fi