Reorder "interesting" pieces of text using sed

Viewed 48

I have a file named file, whose content is

noise
noise
X noise STUFF1 noise STUFF2 noise
noise
Y noise STUFF3 noise
noise

and I assert that X and Y are distinct, that each occur once in file, and that X occurs first.

I'm able to issue a sed command to extract the first pieces of information, the like of

$ sed -n '/X/s/\(.*\)\(…\)\(.*\)\(…\)/\2 \4/p' < file
STUFF1 STUFF2
$ 

and a similar one to extract STUFF3 (¹), but what I'd really like to do is to find the right sed incantation so that

$ sed … < file
STUFF3 STUFF1 STUFF2
$

(and possibly learn, at last! how sed's hold buffer works).


(1) This is not a question on regular expression, I know how to insulate the pieces of text that I need. I need to save the info I've collected and output it at the right time.

3 Answers

Using sed

$ sed -n '/^X/{s/.[^[:upper:]]*\([[:alnum:]]*\)/\1 /g;h};/^Y/{s/.[^[:upper:]]*\([[:alnum:]]*\)/\1 /g;G;s/\n//p}' file
STUFF3  STUFF1 STUFF2
$ cat script.sed
/^X/{                                         #Match line beginning with X
    s/.[^[:upper:]]*\([[:alnum:]]*\)/\1 /g    #As you know how to extract what you need, this is just for your sample data to extract needed strings
    h                                         #Retain the output of the substitution in the hold buffer
}
/^Y/{                                          #Match line beginning with Y
    s/.[^[:upper:]]*\([[:alnum:]]*\)/\1 /g    #Same as above
    G                                         #Append the contents of the hold space
    s/\n//p                                   #Remov the new line
}
sed -nf script.sed file
STUFF3  STUFF1 STUFF2
sed -n '  # Do not print by default
  /X/{ 
     # pattern space holds 'X noise STUFF1 noise STUFF2 noise'
     s/.*\(STUFF1).*\(STUFF2\).*/\1 \2/
     # pattern space holds 'STUFF1 STUFF2'
     # add stuff from pattern space to hold space with __leading newline__
     H
     # hold space holds '\nSTUFF1 STUFF2'
     # use l to inspect
     d
  }
  /Y/{
     s/.*\(…\).*/\1/p
     H
     # hold space holds '\nSTUFF1 STUFF2\nSTUFF3'
     d
  }
  ${  # last line?
     # switch hold space with pattern space
     x
     # we have '\nSTUFF1 STUFF2\nSTUFF3' in paterrn space, let's make it nice with spaces
     s/\n/ /g
     s/  */ /g
     s/^ *//g
     s/ *$//g
     # print it
     p
  }
'

This might work for you (GNU sed):

sed -En '/^X/h;/^Y/{G;s/\s+/ /g;s/.*/echo "&"|cut -d" " -f3,7,9/ep}' file

Make a copy of the line starting X in the hold space.

Append the copy to a line starting Y.

Replace one or more white spaces by a space globally on the above line(s).

Replace the contents of that line by required columns using the cut command.

Related