How to delete blank lines starting from line 5

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

Another option using awk:

awk 'NR<5||/./'

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".

Tags:

Sed