Converting a Bash array into a delimited string

Solution 1:

Because parentheses are used to delimit an array, not a string:

ids="1 2 3 4";echo ${ids// /|}
1|2|3|4

Some samples: Populating $ids with two strings: a b and c d

ids=("a b" "c d")

echo ${ids[*]// /|}
a|b c|d

IFS='|';echo "${ids[*]}";IFS=$' \t\n'
a b|c d

... and finally:

IFS='|';echo "${ids[*]// /|}";IFS=$' \t\n'
a|b|c|d

Where array is assembled, separated by 1st char of $IFS, but with space replaced by | in each element of array.

When you do:

id="${ids[@]}"

you transfer the string build from the merging of the array ids by a space to a new variable of type string.

Note: when "${ids[@]}" give a space-separated string, "${ids[*]}" (with a star * instead of the at sign @) will render a string separated by the first character of $IFS.

what man bash says:

man -Len -Pcol\ -b bash | sed -ne '/^ *IFS /{N;N;p;q}'
   IFS    The  Internal  Field  Separator  that  is used for word splitting
          after expansion and to split  lines  into  words  with  the  read
          builtin command.  The default value is ``<space><tab><newline>''.

Playing with $IFS:

declare -p IFS
declare -- IFS=" 
"
printf "%q\n" "$IFS"
$' \t\n'

Literally a space, a tabulation and (meaning or) a line-feed. So, while the first character is a space. the use of * will do the same as @.

But:

{
    IFS=: read -a array < <(echo root:x:0:0:root:/root:/bin/bash)
    
    echo 1 "${array[@]}"
    echo 2 "${array[*]}"
    OIFS="$IFS" IFS=:
    echo 3 "${array[@]}"
    echo 4 "${array[*]}"
    IFS="$OIFS"
}
1 root x 0 0 root /root /bin/bash
2 root x 0 0 root /root /bin/bash
3 root x 0 0 root /root /bin/bash
4 root:x:0:0:root:/root:/bin/bash

Note: The line IFS=: read -a array < <(...) will use : as separator, without setting $IFS permanently. This is because output line #2 present spaces as separators.

Solution 2:

You can use printf too, without any external commands or the need to manipulate IFS:

ids=(1 2 3 4)                     # create array
printf -v ids_d '|%s' "${ids[@]}" # yields "|1|2|3|4"
ids_d=${ids_d:1}                  # remove the leading '|'