Joining strings in POSIX shell

A common programming task is to glue (or "join") items together to create a single string. For example:

>>> ', '.join(['foo', 'bar', 'baz'])
"foo, bar, baz"

Notice that we have three items but only two commas — this can be important if the tool we passing doesn't support trailing delimiters or we simply want the result to be human-readable.

Unfortunately, this can be inconvenient in POSIX shell where we construct strings via explicit concatenation. A naïve solution of:

RESULT=""
for X in foo bar baz
do
    RESULT="${RESULT}, ${X}"
done

... incorrectly returns ", foo, bar, baz". We can solve this with a (cumbersome) counter or flag to only attach the delimiter when we need it:

COUNT=0
RESULT=""
for X in foo bar baz
do
    if [ "${COUNT}" = 0 ]
    then
        RESULT="${X}"
    else
        RESULT="${RESULT}, ${X}"
    fi
    COUNT=$((COUNT + 1))
done

One alternative is to use the little-known ":+" expansion modifier. Many people are familiar with ":-" for returning default values:

$ echo ${VAR:-fallback}

By contrast, the ":+" modifier inverts this logic, returning the fallback if the specified variable is actually set. This results in the elegant:

RESULT=""
for X in foo bar baz
do
    RESULT="${RESULT:+${RESULT}, }${X}"
done

Comments (4)

Bash User

Consider using Bash arrays with slicing.

ITEMS=(foo bar baz)
echo "${ITEMS[0]}$(test ${#ITEMS[@]} -ge 2 && printf ", %s" ${ITEMS[@]:1})"

foo, bar, baz

Sept. 11, 2015, 6:42 a.m. #
Not in a post with "POSIX shell" in the title.. Interestingly, that doesn't look any cleaner either.

Or:

RESULT="${RESULT}${RESULT:+, }${X}"

This is equivalent in the number or references to variable RESULT, but I find it a tiny bit more readable.

Sept. 11, 2015, 8:05 a.m. #
I prefer how the solution in the post body groups together all the "join" apparatus in one place - here, most of the line is concerned with quasi-noise relating to that.
Jeroen

my usual solution is

SEP=''
RESULT=""
for X in foo bar baz
do
RESULT=$RESULT$SEP$X
SEP=", "
done

a few more lines, and it clobbers SEP, but no conditionals needed.

Sept. 12, 2015, 9:40 a.m. #
j_hn

... or drop the last ", " from the result:

result=""
for w in foo bar; do
result=${result}, ${w}
done
result=${result%, }

Sept. 16, 2015, 8:35 a.m. #