Replacing returns with commas with sed

Someone came to my website by way of the search engine query:

sed replace return with comma

It's an interesting dilemma. There are two obstacles to doing this in sed. One is that sed operates line by line and never sees the newline endings. Second, sed's regular expressions don't provide a way to exactly match a newline character.

If you really want to do this, don't use sed; use tr.

cat file | tr '\n' ','

But let's work with sed. We can regain the newlines by appending each line onto the hold space. (Again, if you want efficient, use tr.) Then you can convert all the whitespace characters with the POSIX character class [:space:]. But, of course, the text probably contains spaces and tabs that you don't want to convert. So you have to change those to something innocuous first, convert what's left, and change the spaces and tabs back.

This code changes every space to {{{{space}}}} and every tab to {{{{tab}}}}; stores the first line in the hold space; appends every other line to the hold space; and on the last line it changes all the remaining whitespace characters to a comma, then the space-markers and the tab-markers back to spaces and tabs.

s/ /{{{{space}}}}/g
s/	/{{{{tab}}}}/g
1h
2,${H}
${g;s/[[:space:]]/,/g;
    s/{{{{space}}}}/ /g;s/{{{{tab}}}}/	/g;p}

There remains a problem with line feeds and any other whitespace besides newline, space, and tab. I can't find details on what [:space:] actually includes, but it seems to be locale-specific. If you have form feeds in your input, you'll need to escape them like we did the spaces and tabs.

Pleasantly, unlike a newline, form feeds can be inserted meaningfully into sed code (using something like quoted-insert (C-q) in Emacs). Then they can be manipulated just like spaces and tabs.

s/ /{{{{space}}}}/g
s/	/{{{{tab}}}}/g
s//{{{{form-feed}}}}/g
1h
2,${H}
${g;s/[[:space:]]/,/g;
    s/{{{{space}}}}/ /g;
    s/{{{{tab}}}}/	/g;
    s/{{{{form-feed}}}}//g;p}

Update

We can match the newline character using shell substitution, but I can't get 1h;2,$H;... to work, so this code creates an extraneous leading comma.

sed -n -e "H;\${g;s/\n/,/g;p}" foo.txt

Mr. Stolz wrote and provides the correction:

sed -n '1h;2,$H;${g;s/\n/,/g;p}' foo.txt