22
VAR=a,b,c,d
# VAR=$(echo $VAR|tr -d '\n')
echo "[$VAR]"
readarray -td, ARR<<< "$VAR"
declare -p ARR

Result:

[a,b,c,d]
declare -a ARR=([0]="a" [1]="b" [2]="c" [3]=$'d\n')

How can I tell readarray not to add the final newline \n? What is the meaning of the latest $ symbol?

1

3 Answers 3

33

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")
1
  • 3
    Wow! I would have never found this. Thanks May 20, 2019 at 10:25
5

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).

3
  • The readarray <<< "$string" writes to a tempp file and then deletes it instead of using the memory? May 20, 2019 at 11:34
  • @user1156544 Yes.
    – user313992
    May 20, 2019 at 12:03
  • How can I do something similar to your last command in bash? I am using bash 3.3 and trying to compensate the lack of readarray -d May 23, 2019 at 19:26
4

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).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .