Converting a Bash array into a delimited string
# REVISION: 2017-03-14
# Use of read and other bash specific features (bashisms)
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
:
set | grep ^IFS=
IFS=$' \t\n'
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:
{
# OIFS="$IFS"
# IFS=$': \t\n'
# unset array
# declare -a array=($(echo root:x:0:0:root:/root:/bin/bash))
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.
Your first question is already addressed in F. Hauri's answer. Here's canonical way to join the elements of an array:
ids=( 1 2 3 4 )
IFS=\| eval 'lst="${ids[*]}"'
Some people will cry out loud that eval
is evil, yet it's perfectly safe here, thanks to the single quotes. This only has advantages: there are no subshells, IFS
is not globally modified, it will not trim trailing newlines, and it's very simple.
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 '|'