You could preserve the trailing empty lines by using readarray
to save the data, and printf
for output.
Something like:
readarray last_n2_lines < <(tail -n+3 $file)
printf "%s" "${last_n2_lines[@]}" > ${file}.new
Demo:
$ cat test; echo end
1
2
3
4
end
$ tail -n+3 test ; echo end
3
4
end
$ readarray data < <(tail -n+3 test)
$ printf "%s" "${data[@]}" ; echo end
3
4
end
Explanation:
readarray data
reads lines from standard input into the array called data
.
cmd1 < <(cmd2 param1 param2 ...)
redirects the output of cmd2 param1 param2 ...
to the standard input if cmd1
. Careful with the syntax: it must be < [space] <(...)
, no space between <(
, space required between the two < <
.
So after the first line, data
contains all the lines that tail -n+3
output. Each item in the array is a line of the input with the line terminator included.
Here's how you access each element. Notice that two lines are printed for each item defined in the array: the one in the array (which contains a newline), and the newline added by echo
.
$ echo "${data[0]}"
3
$ echo "${data[1]}"
4
$ echo "${data[2]}"
$ echo "${data[3]}"
$ echo "${data[4]}" # out of bounds
$
${data[@]}
expands to all elements of the array data
printf "%s" param1 param2 ...
prints all of its arguments unchanged (i.e. doesn't add or remove line terminators).
So the second statement prints back everything that was read in, unchanged.