How to remove new line added by readarray when using a delimiter?

The implicit trailing new-line character is not added by the readarray builtin, but by the here-string (<<<) of bash, see Why does a bash here-string add a trailing newline char?. You can get rid of that by printing the string without the new-line using printf and read it over a process-substitution technique < <()

readarray -td, ARR < <(printf '%s' "$VAR")
declare -p ARR

would properly generate now

declare -a ARR=([0]="a" [1]="b" [2]="c" [3]="d")

You could use split+glob (what happens when you leave an expansion unquoted in list contexts). It gets in our way most of the time, it would be a shame not to use it when we actually need it:

IFS=,
set -o noglob

ARR=($VAR) # split+glob with glob disabled, and split using , as delimiter

That's a bit less convoluted than writing a temp file and then call readarray on it like in the readarray <<< "$string" approach (also note that readarray -d needs a very recent version of bash).

Note that despite the S in IFS (which stands for separator), that works the same way as readarray in that a,,b, is split into "a", "" and "b" only.

For a real splitting operator, you could use zsh instead:

ARR=("${(@s:,:)VAR}")

(the @ and double quotes to preserve the empty elements).


A canned version of @StéphaneChazelas' answer:

# usage: setarray varname sep string
setarray(){ declare -n a=$1; local IFS=$2 -; set -f; a=($3); }

$ setarray arr , 1,2,3,
$ declare -p arr
declare -a arr=([0]="1" [1]="2" [2]="3")

$ setarray path : "$PATH"
$ setarray ld_preload ': ' "$LD_PRELOAD" # its elements can be separated by either ':' or spaces
...

The local - will make options like set -f (noglob) local to the function, just as variables.

The declare -n a=$1 will create a local variable a as an alias to the global variable named by $1 (the first argument to the function).