6

I am dealing with a situation where I need to create a comma separated list from an array into a heredoc and remove the last comma. I am using bash for piped into sed which is erasing all commas instead of the last one. A simplified example is as follows:

x=$(for i in a b c; do echo "${i}",; done| sed 's/,$//')
echo $x
a b c

Desired output:

a, b, c

Any suggestions appreciated.

6

The issue was that, by default, echo adds a new line each time it is called and sed was operating on those new lines. You couldn't see that because, when bash processes $(...), those new lines are converted to spaces. So, to fix the problem while making the smallest change to your approach:

$ x=$(for i in a b c; do echo -n "${i}",; done| sed 's/,$//') ; echo $x
a,b,c

The option -n tells echo not to add new lines.

If you want spaces between the items, they are easily added:

$ x=$(for i in a b c; do echo -n "${i}, " ; done| sed 's/, $//') ;echo $x
a, b, c
9

One option is to stick with bash

arr=(a b c)
x=$(IFS=,;printf  "%s" "${arr[*]}")
echo "$x"
a,b,c

Alternately

arr=(a b c)
printf -v x "%s," "${arr[@]}"
x=${x%,}
echo "$x"
a,b,c

With the second option, you can set the separator to ,(comma followed by space) instead of , alone

printf -v x "%s, " "${arr[@]}"
x=${x%, }
echo "$x"
a, b, c
  • Curious: what do you mean be stick with bash? Isn't my approach already bash? – mkc Apr 29 '14 at 20:56
  • @Ketan, I meant an approach that does not involve calling out to tools such as sed – iruvar Apr 29 '14 at 20:58
4

another useful technique for this is paste -s:

$ arr=(a b c)
$ printf "%s\n" "${arr[@]}" | paste -sd,
a,b,c

(note that the paste -sd, here is a very general "turn this set of newline-separated items into a comma-separated list" operation)

  • You should be able to avoid the loop with printf "%s\n" "${arr[@]}" | paste -sd, – iruvar Apr 29 '14 at 21:10
1
$ w=(a b c)

$ IFS=, eval 'echo "${w[*]}"'
a,b,c

Side note, aren’t we reinventing the wheel here?

0

For the array a=(1 2 3 4 5)

If you can modify the array:

for (( i=$((${#a[@]}-1)); i-->0; )); do a[i]+=","; done
echo "${a[@]}"
1, 2, 3, 4, 5

$(( ${#a[@]} - 1 )) calculates the last-but-one array index and a[i]+="," appends a string.

If you want to do it in a function:

print_with_commas() {
        while [[ $# -gt 1 ]];do echo -n "$1, ";shift;done;echo "$1"
}
print_with_commas "${a[@]}"
1, 2, 3, 4, 5

Pop off each argument to the function, except the last, and echo with ", " appended with no newline. Then echo the last argument and newline.

0

check the following script

$ cat run.sh
#!/bin/bash
echo example 1
for i in a b c; do echo "${i},"; done
echo example 2
for i in a b c; do echo "${i},"; done|sed '$s/,//'
echo example 3
for i in a b c; do echo "${i}"; done|sed '$!s/$/,/'
echo example 4
SEP=''; for i in a b c; do printf "${SEP}${i}"; SEP=',\n'; done; printf '\n'
echo example 5
STR=''; SEP=''; for i in a b c; do STR="${STR}${SEP}${i}"; SEP=,; done; echo $STR
echo example 6
echo 'a,b,c,'||sed 's/^\(.*\),\(^,\)*$/\1\2/'
echo example 7
echo 'a,b,c,'|sed 's/.\w*$//'

It gives the following output

$ . run.sh
example 1
a,
b,
c,
example 2
a,
b,
c
example 3
a,
b,
c
example 4
a,
b,
c
example 5
a,b,c
example 6
a,b,c
example 7
a,b,c

example 1: our for-loop actually produces lines. The echo $x transforms this to a single line.

example 2: You can tell sed to process only the last line

example 3: You can tell sed to process all but the last line

example 4: you can program your for loop that you do some special processing when it is ececuted the first time: dont prepent ',\n' to the line. But in many cases you don't know when it is exectued the last time

example 5: the same as example 4 but you do not produce lines but simply a string

example 6: tell sed to remove the last occurrence of a string in a line

example 7: tell sed to remove the last non whitespace character of a line

-1

Why do you absolutely need to remove the last comma?

You're messing things up and then trying to clean up the mess afterward, instead of thinking correctly about the problem in the first place.

There is a solution when you look at the comma as not being located after each item but in front of each item.

for i in a b c; do
  u="${u:+$u, }$i"
done
echo $u

See bash parameter substitution.

Result:

a, b, c

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.