5

Example: Is there a way to use sed to remove/subsitute a pattern in a file for every 3n + 1 and 3n+ 2 line?

For example, turn

Line 1n/
Line 2n/
Line 3n/
Line 4n/
Line 5n/
Line 6n/
Line 7n/
...

To

Line 1 Line 2 Line 3n/
Line 4 Line 5 Line 6n/
...

I know this can probably be handled by awk. But what about sed?

Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
user3805884
  • 125
  • 2
  • 8
  • 2
    Is `n/` literal there or was that an attempt at a "newline"? – Etan Reisner Aug 19 '15 at 01:27
  • em... i can think of a way to achieve your specific example. but it's better to clarify what on earth you want to achieve. – Jason Hu Aug 19 '15 at 01:28
  • GNU `sed` has a way of specifying what you want directly, but I don't only use GNU `sed` so I've not spent the time learning it. It's something like `1~3` matches the first line and every third line after that. See [addresses](http://www.gnu.org/software/sed/manual/sed.html#Addresses) in the GNU `sed` manual for the details. – Jonathan Leffler Aug 19 '15 at 02:48
  • in fact, the title should read `Remove all line breaks except every nth [...]` – Andre Holzner May 21 '20 at 08:57

3 Answers3

8

Well, I'd just use awk for that1 since it's a little more complex but, if you're really intent on using sed, the following command will combine groups of three lines into a single line (which appears to be what you're after based on the title and text, despite the strange use of /n for newline):

sed '$!N;$!N;s/\n/ /g'

See the following transcript for how to test this:

$ printf 'Line 1\nLine 2\nLine 3\nLine 4\nLine 5\n' | sed '$!N;$!N;s/\n/ /g'
Line 1 Line 2 Line 3
Line 4 Line 5

The sub-commands are as follows:

  • $!N will append the next line to the pattern space, but only if you're not on the last line (you do this twice to get three lines). Each line in the pattern space is separated by a newline character.
  • s/\n/ /g replaces all the newlines in the pattern space with a space character, effectively combining the three lines into one.

1 With something like:

awk '{if(NR%3==1){s="";if(NR>1){print ""}};printf s"%s", $0;s=" "}'

This is complicated by the likelihood you don't want an extraneous space at the end of each line, necessitating the introduction of the s variable.

Since the sed variant is smaller (and less complex once you understand it), you're probably better off sticking with it. Well, at least up to the point where you want to combine groups of 17 lines, or do something else more complex than sed was meant to handle :-)

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
2

The example is for merging 3 consecutive lines although description is different. To generate the example output, you can use awk idiom

  awk 'ORS=NR%3?FS:RS' <(seq 1 9)  
  1 2 3
  4 5 6
  7 8 9

in your case the record separator needs to be defined upfront to include the literals

awk -v RS="n/\\n" 'ORS=NR%3?FS:RS'
karakfa
  • 66,216
  • 7
  • 41
  • 56
0

ok. following are ways to deal with it generally using awk and sed.

awk:

awk 'NR % 3 { sub(/pattern/, substitution) } { print }' file | paste -d' ' - - -

sed:

sed '{s/pattern/substitution/p; n;s/pattern/substitution/p; n;p}' file | paste -d' ' - - -

both of them replace pattern in 3n+1 and 3n+2 lines into substitution and keep the 3n line untouched.

paste - - - is the bash idiom to fold the stdout by 3.

Jason Hu
  • 6,239
  • 1
  • 20
  • 41