1

I am having some difficulty getting my SED script to work properly. It appears to only work on the first occurrence. I am basically a UNIX beginner - please bear with me.

Data file looks like this:

exec cics 
end-exec.
exec cics 
send map
end-exec.
exec cics 
end-exec.

Actual output is as follows and appears to work correctly only on the first occurrence:

exec cics end-exec.
exec cics 
send map
end-exec.
exec cics 
end-exec.

Desired output should be as follows:

exec cics end-exec.
exec cics send map end-exec.
exec cics end-exec.

Everything starting with "exec cics" and ending with "end-exec" should be on one line with any newlines removed.

SED script is as follows:

/exec cics/,/end-exec/{
:a    
N
$!ba
s/\n//
}

I had got the code within the curly braces from here: How can I replace a newline (\n) using sed?

My initial script did not have the :a;N;$!ba. Can anyone see what I am missing or doing wrong?

Community
  • 1
  • 1
rainman168
  • 21
  • 2

3 Answers3

2

The result that you get can be explained by the fact that you substitute only the first newline:

s/\n//

However, even if you performed the substitution globally, i.e. used:

s/\n//g

you'd get:

exec cics end-exec. exec cics  send map end-exec. exec cics  end-exec.

because the $ address would match the last occurrence in the file.

Instead of branching to the label a unless you match the last line, don't branch when you encounter end-exec. Saying:

sed '/exec cics/,/end-exec/{:a;N;/end-exec/!ba;s/\n/ /g}' filename

would produce:

exec cics end-exec.
exec cics send map end-exec.
exec cics end-exec.

If your input consists of contiguous blocks starting from exec cisc and ending with end-exec, you could simplify it:

sed ':a;N;/end-exec/!ba;s/\n/ /g' filename
devnull
  • 118,548
  • 33
  • 236
  • 227
1

If you like to try awk

awk '{printf (/exec cics/ && NR>1?RS:"")"%s",$0} END {print ""}'
exec cics end-exec.
exec cics send mapend-exec.
exec cics end-exec.

If line starts with exec cics and not is first line, add newline before the line.
Else, just print the data in one line.

Jotne
  • 40,548
  • 12
  • 51
  • 55
1

This modified version of your sed script seems to do the job:

/exec cics/,/end-exec/{
    :a
    /end-exec/! N
    s/\n/ /g
    t a
}

When saved in the file sed.script, and given the data file (data2):

exec cics
end-exec.
exec cics
send map
end-exec.
exec cics
end-exec.
exec cics
do this
and that
and tother
end-exec.

(with no trailing blanks on any of the data lines), then I get:

$ sed -f sed.script data2
exec cics end-exec.
exec cics send map end-exec.
exec cics end-exec.
exec cics do this and that and tother end-exec.
$

What does the script do?

  1. For each range of lines between exec cics and end-exec,
  2. Set label a,
  3. If the pattern space doesn't contain end-exec add another line to it.
  4. Replace any newlines with spaces.
  5. If there was a substitution, jump back to label a.

While debugging/devising this, I added a couple of extra lines after the t and before the }:

s/^/[[/
s/$/]]/

This helped me see how the data was handled with various other versions of the commands, including the original; the lines were enclosed in [[ and ]] as they were printed.

Tested on Mac OS X 10.9.2 Mavericks using the BSD sed that's supplied.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • +1 for the explanation (and for making it work with BSD `:)`). The `g` flag in the solution is redundant though, since we are running the substitution every time after appending the next line. – jaypal singh May 11 '14 at 23:40
  • @jaypal: yes, the global suffix on the substitute is not needed, though it is unlikely to do measurable harm being present. One version of the script needed it, I think, but it was long enough ago now that I don't remember for sure. – Jonathan Leffler May 12 '14 at 00:49
  • Certainly, no harm done by it being present. I was just thinking along the lines that instead of doing the substitution on every line that is appended, a better and less expensive approach would be to build our entire line first and then do a global substitution just once. The second solution proposed by [devnull](http://stackoverflow.com/a/23595491/970195) seems to do that, though it is GNU `sed` only. – jaypal singh May 12 '14 at 00:59