10

I have this:

sed -i '/^$/d' temp_spec.rb

which is stripping blank lines and works well. How can I make it only do this for lines 5-999 (or ideally 5 to end-of-file).

I tried:

sed -n5,999 -i '/^$/d' temp_spec.rb
sed '5,999!d/^$/d' temp_spec.rb

but neither worked (no errors).

  • Just to be sure: Do you want to keep lines 1 to 4, no matter whether they are blank or not, or do you want to delete lines 1 to 4, no matter whether they are blank or not? (So far, I assumed the former, but the second command you tried makes me wonder.) – Uwe May 16 '13 at 23:13
12

If you want to delete all blank lines starting with line 5 and keep lines 1 to 4, you can use

sed -i '5,${;/^$/d;}' temp_spec.rb

The { is the grouping operator, so the first command 5,${ means "from line 5 until the end of input ($) execute the following commands until the matching }". The commands between { and } can again be prefixed by addresses, so the inner command /^$/d means "if there is nothing between beginning (^) and end ($) of the line, delete it". Sed commands can be separated by ;. (This is a badly documented feature of sed. It's supported by most sed implementations, but it's not entirely portable.) As noted by Hauke, the ; after { is optional; the one before } is required, though.

If you want to delete all blank lines starting with line 5 and also delete lines 1 to 4, it's easier:

sed -i '1,4d;/^$/d' temp_spec.rb
  • Nice. I didn't know that addresses can be nested that way. For me it works without the leading;, too. – Hauke Laging May 16 '13 at 18:58
  • Yep, you're right about the first ;. – Uwe May 16 '13 at 19:00
  • @Hauke Laging You can separate commands in a sed script by ';' instead putting them on the separate lines. That is an "undocumented" feature. – Predrag Punosevac May 18 '13 at 2:17
4

Another option using awk:

awk 'NR<5||/./'
2
sed '5,${s/^$//; t delete; b end; : delete; d; : end;}' temp_spec.rb

Edit 1:

I am supposed to explain this, thus...

This is unnecessarily complicated. I didn't know that address ranges are allowed within {}. So I had to express "delete empty lines" differently. The core command is t which is sed's way of if ... then. T would have been easier but is available for GNU sed only. I cite the man page:

t label : If a s/// has done a successful substitution since the last input line was read and since the last t or T command, then branch to label; if label is omitted, branch to end of script.

I abuse the famous s command. It shall not replace anything but test only whether the line is empty. So it replaces an empty line by an empty line (could use anything as replacement as the line is deleted anyway).

If s has done a "replacement" then the line is empty. In that case the command d shall be executed. Otherwise nothing is to be done. As t does not jump in case of an s action I need the branch command b to jump to the end of the script. : label are branch targets. Like goto back then in the dark ages (when sed was invented... te-hee).

Another option would be to have s "replace" all non-empty lines, making the s more complicated but rest of the command easier:

sed '5,${s/^\(..*\)$/\1/; t end; d; : end;}' input

^..*$ means "non-empty line" and \1 means "the content of the first brackets".

  • A bit of explanation for OP's sake (I can feel their brain trying to crawl out through the ears reading this...) – vonbrand May 16 '13 at 19:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.