Bash mapfile array count different
I did the following:
mapfile -t array < <(commands)
where commands
will produce new-line separated output.
Then based on the result, if there’s nothing in the array, exit, otherwise continue:
[[ "${#array[@]}" -eq 0 ]] && exit
...
The problem is that the condition is never met. Because when the said commands
returns nothing, the ${#array[@]}
returns 1 instead of 0.
But it counts correctly if I assign the array as follows:
array=($(commands))
My tests:
$ declare -a array
$ array=($(echo))
$ echo ${#array[@]}
0
$ unset array
$ mapfile -t array < <(echo)
$ echo ${#array[@]}
1
Why?
Initially I used the array=($(commands))
format but shellcheck
suggested me to use mapfile
instead.
GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)
This is what you get if the command inside the prints nothing:
$ mapfile -t array < <(printf ''); echo "num: ${#array[@]}"; declare -p array
num: 0
declare -a array=()
This is what you get if it prints a single empty line:
$ mapfile -t array < <(echo); echo "num: ${#array[@]}"; declare -p array
num: 1
declare -a array=([0]="")
The empty line produces an empty element, quite appropriately.
If your program produces such empty lines and you can’t stop it, you could remove them with e.g. grep
. Or check for the case of a single array element that’s empty.
Remove the empty lines with grep
:
$ mapfile -t array < <(echo | grep .); echo "num: ${#array[@]}"; declare -p array
num: 0
declare -a array=()
Check for the empty element:
$ mapfile -t array < <(echo);
$ if [[ "${#array[@]}" = 0 || "${#array[@]}" = 1 && "${array[0]}" = "" ]]; then
echo "empty list or only one empty element";
fi
empty list or only one empty element
In
array=($(commands))
it’s different, since the result of the command substitution goes through word splitting, which also removes any trailing whitespace. And splits lines with whitespace in them. (The command substitution itself also removes any trailing newlines, not that it matters here.)