2

It has always bugged me that sed doesn't have a j command, like ed does.

This question asks how to join lines in general, and there are a bunch of good answers, many featuring other tools like awk and perl. And there are some sed solutions, too, but they all involve using the s command to literally edit out the newline, which is a solution that for various reasons strikes me as kludgey.

Now, sed also has some obscure commands having to do with "hold" and "pattern" spaces, which I've never figured out how to use. I have always assumed -- BICBW -- that the hold space is sed's answer to the j command, if only I could figure out how to use it.

So my question for today -- and this is as much on an intellectual as a practical question -- is, is there a good, safe, reasonably concise way to do the equivalent of

sed /pat/j

That is, for every line containing the pattern pat, join it to the following line (that is, just as ed would if I used g/pat/j/).

As I said, this is somewhat of an intellectual question. I know how to join lines using other tools, or by using sed and the s command on a quoted literal newline. But it seems to me there ought to be a better way.

(Or maybe I should just snarf a copy of sed.c and hack in a proper j command.)

Community
  • 1
  • 1
Steve Summit
  • 45,437
  • 7
  • 70
  • 103

2 Answers2

3
sed  '/pat/{$!{N;s/\n/ /}}'

If the pat matches and line is not last line, add following line to this line and then replace \n with space

Ref: how-can-i-use-sed-to-replace-a-multi-line-string

Community
  • 1
  • 1
jijinp
  • 2,592
  • 1
  • 13
  • 15
  • Aha! So if I'm understanding this correctly, `N` is almost like `j`, except it inserts a newline. Very interesting. (Your example didn't work for me directly; I had to use `sed -e '/pat/N' -e 's/\n/ /'`. Your example keeps complaining "bad flag in substitute command: '}'". Dunno if that's a limitation with my version of sed or what.) – Steve Summit Apr 22 '16 at 15:05
  • It's using non-portable constructs so the behaviors in different seds will vary. So is the solution in your comment and will likewise behave differently in different seds. – Ed Morton Apr 23 '16 at 02:10
  • @SteveSummit He's using GNU `sed` which doesn't require newlines after each command. Handy for "one-liners". – Kusalananda Jul 08 '16 at 16:48
2

I know it's hard to swallow but honestly - sed is for simple substitutions on individual lines and for anything else you should be using awk. I find sed incredibly useful and have used it almost every day for 30+ years but all of the sed constructs to do anything other than simple substitutions on individual lines became obsolete in the mid-1970s when awk was invented.

$ seq 4 | awk '{printf "%s%s", s, $0; s=ORS} /2/{s=OFS} END{print ""}'
1
2 3
4

How about joining lines by 2s:

$ seq 10 | awk '{printf "%s%s", $0, (NR%2?OFS:ORS)}'
1 2
3 4
5 6
7 8
9 10

Try modifying a sed script that joins 2 lines to join 5 lines instead. Compare to the simple, obvious awk tweak of changing the number 2 to the number 5:

$ seq 10 | awk '{printf "%s%s", $0, (NR%5?OFS:ORS)}'
1 2 3 4 5
6 7 8 9 10

The above will work in all awks on all OSes and are easily enhanced with minimal changes if/when your requirements change - two very important reasons why you should be learning to use awk for this instead of sed.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
  • Thanks, Ed, I know all about awk, use it every day, but that's not what I asked. – Steve Summit Apr 23 '16 at 08:39
  • @SteveSummit understood but this question and the answers it gets will be here for years to come and read by many people so IMHO it's important that this answer be present for their sake if not for yours. – Ed Morton Apr 23 '16 at 13:14