Since less 530 (released in December 2017), less --quit-if-one-screen
does not switch to the alternate screen if it reads less than one screenful. So you won't have this problem if your version of less is recent enough.
In earlier versions, less has to decide whether to use the alternate screen when it starts. You can't defer that choice to when it terminates.
You could call less, let it use the alternate screen, and cat the content onto the primary screen if less terminates automatically. However I don't know of a way to detect automatic termination.
On the other hand, it isn't that difficult to call cat for short inputs and less for larger inputs, even preserving buffering so that you don't have to wait for the whole input to start seeing stuff in less (the buffer may be slightly larger — you won't see anything until you have at least one screenful of data — but not much more).
#!/bin/sh
n=3 # number of screen lines that should remain visible in addition to the content
lines=
newline='
'
case $LINES in
''|*[!0-9]*) exec less;;
esac
while [ $n -lt $LINES ] && IFS= read -r line; do
lines="$lines$newline$line"
done
if [ $n -eq $LINES ]; then
{ printf %s "$lines"; exec cat; } | exec less
else
printf %s "$lines"
fi
You might prefer to see the lines on the main screen as they come in, and switch to the alternate screen if the lines would cause scrolling.
#!/bin/sh
n=3 # number of screen lines that should remain visible in addition to the content
beginning=
newline='
'
# If we can't determine the terminal height, execute less directly
[ -n "$LINES" ] || LINES=$(tput lines) 2>/dev/null
case $LINES in
''|*[!0-9]*) exec less "$@";;
esac
# Read and display enough lines to fill most of the terminal
while [ $n -lt $LINES ] && IFS= read -r line; do
beginning="$beginning$newline$line"
printf '%s\n' -- "$line"
n=$((n + 1))
done
# If the input is longer, run the pager
if [ $n -eq $LINES ]; then
{ printf %s "$beginning"; exec cat; } | exec less "$@"
fi