1

When you use grouped commands in a sed script, is there a way to reference the first and last line of the range you're in?

I want to be able to print the first and last line of a range as well as selected lines between those.

#n
/StartLinePattern/,/EndLinePattern/{
  /PatternOfSubLineToPrint/p;
}

I know that I can solve this by including another grouped command that matches the first and last lines of the range (again); but it would be cleaner, faster, and more reusable to do something akin to the standard for non-grouped commands.

1p;$p

I tried including the above in the group, but it doesn't work. It appears that 1 and $ are absolute, not relative to the range you're in.

Background

I made a sed script that filters elements out of an XML file. To do this, I use ranges with grouped commands to print certain sub elements within that range. So the script works by printing everything that you want to keep.

#n
/<Parent\>/,<\/Parent>/{
  /<Child1\>/,/<\/Child1>/p;
  /<Child2\>/,/<\/Child2>/p;
  /<SingleLineChild\>/p;
}
5
  • 1
    Better add sample XML input, expected output. sed is not a XML parser Commented Jan 18, 2023 at 17:54
  • @GillesQuenot The question has nothing to do with XML. The question on the last line of the original post is universal.
    – Sildoreth
    Commented Jan 18, 2023 at 17:59
  • 1
    This is a very bad idea and it is impossible to do correctly (OK, it's possible but very, very hard even for experts). If you have XML, use an XML parser nor ad hoc regular expression parsing.
    – terdon
    Commented Jan 18, 2023 at 17:59
  • For my input files, I can assume that start and end tags are on their own line or both on the same line. So the problem is as simple as "which lines do I want to keep"? Again, the question has nothing to do with XML.
    – Sildoreth
    Commented Jan 18, 2023 at 18:00
  • 1
    So remove XML and use simple lines of plain text Commented Jan 18, 2023 at 18:08

1 Answer 1

2

You could do that by using an empty regex i.e. // as the first regex after the opening {
e.g with an input like:

hello
world
start
inner1
inner2
inner3
end
outer

if you run

sed -n '/start/,/end/{
//p
/inner1/p;/inner3/p
}' infile

it prints

start
inner1
inner3
end

You can see how that works here... Just to repost the important part:

When a REGEX is empty (i.e. //) sed behaves as if the last REGEX used in the last command applied (either as an address or as part of a substitute command) was specified.


If you wanted to exclude either the start of the range or the end you'd just add another test:

sed -n '/start/,/end/{
//{
/start/!p
}
/inner1/p;/inner3/p
}' infile
2
  • Yes! This is exactly what I needed, so I'm marking as the accepted answer. It's too bad it doesn't cover the case where you want only the first or last line of the range.
    – Sildoreth
    Commented Jan 18, 2023 at 18:49
  • @Sildoreth - that's easy to manage, see edit. Commented Jan 19, 2023 at 13:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .