Converting a Bash array into a delimited string

I would like to know the following;

  1. Why the given non-working example doesn't work.
  2. If there are any other cleaner methods than those given in working example.

Non-working example

> ids=(1 2 3 4);echo ${ids[*]// /|}
1 2 3 4
> ids=(1 2 3 4);echo ${${ids[*]}// /|}
-bash: ${${ids[*]}// /|}: bad substitution
> ids=(1 2 3 4);echo ${"${ids[*]}"// /|}
-bash: ${"${ids[*]}"// /|}: bad substitution

Working example

> ids=(1 2 3 4);id="${ids[@]}";echo ${id// /|}
1|2|3|4
> ids=(1 2 3 4); lst=$( IFS='|'; echo "${ids[*]}" ); echo $lst
1|2|3|4

In context, the delimited string to be used in a sed command for further parsing.

Answers


# 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 '|'

An utility function to split arguments array with a delimiter string:

# Split arguments on delimiter
# @Params
# $1: The delimiter string
# $@: The arguments to delimit
# @Output
# >&1: The arguments separated by the delimiter string
split() {
  (($#<2)) && return 1 # At least 2 arguments required
  local -- delim="$1" str
  shift
  printf -v str "%s$delim" "$@"
  echo "${str:0:-${#delim}}"
}

my_array=( 'Paris' 'Berlin' 'London' 'Brussel' 'Madrid' 'Oslo' )

split ', ' "${my_array[@]}"

Output:

Paris, Berlin, London, Brussel, Madrid, Oslo

Need Your Help

Peek ahead when iterating an array in PHP

php arrays foreach spl

Is it possible to "peek ahead" while iterating an array in PHP 5.2? For example, I often use foreach to manipulate data from an array:

spring mvc not returning json content - error 406

java jquery spring spring-mvc jackson

I am using Spring MVC with JSON as specified in Ajax Simplification Spring 3.0 article.