First, let me state that this is a programming question (and thus does not belong on superuser et. al.) because I'm talking shell programming. This could almost be a golf question, but I do not have an answer to begin with, so any help would be appreciated :-)
So, the story is: I like to pipe stuff into less
with the --quit-if-one-screen
option because it is very comfortable: less
does not get in my way when unnecessary. Or does it ? When my prompt is already at the bottom of my terminal window, this option does exactly what I want (i.e. less
behaves like cat
). But, when my current prompt is at the top of the window, less
first prints plenty of blank lines to clear the screen, then prints out my (short) file at the bottom of the screen, and only then it realizes that there is less text than one screen, so it exits and I get my prompt back.
But this behaviour is not great, because of all those useless blank lines. I tried different options, or wrote scripts and aliases, and the best I could come up with would be this (I'm using zsh, so the shell is already capable of duplicating pipes and so on):
function catless() {
cat \
>>( bucket -$LINES | cat ) \
>>( bucket +$LINES | less )
}
Where bucket
is another script I just wrote, which copies stdin to stdout if it is less than N lines (with -N) or more than N (with +N).
I posted it here: http://snipt.net/Gyom/copy-stdin-to-stdout-or-not-depending-on-length
And ls | catless
almost-works. But, for synchronization reasons, the different processes involved here do not get access to the terminal correctly and everything executes in the background (in particular, I never get a correct less
here, and the prompt comes back too soon). But maybe I took the wrong path.
So, to summarize, what I want is such a function/script/whatever that I can type ls | catless
and it behaves exactly like ls | cat
when the output of ls
is shorter than one screen, and like ls | less
when longer.
Any ideas ?
The -X
flag might help you out (from less(1)):
-X or --no-init Disables sending the termcap initialization and deinitialization strings to the terminal. This is sometimes desirable if the deinitialization string does something unnecessary, like clearing the screen.
So, the following should do what you want:
export LESS="-E -X"
Or, since you like --quit-if-one-screen, you could instead:
export LESS="-F -X"
In the news for less version 406, I see “Don't move to bottom of screen on first page.”. Which version do you have? My system version is 382 and it moves to the bottom of the screen before printing (causing blank lines if there is only one screenful and -F
is used).
I just installed version 436, and it seems to do what you want when given -FX
(put it in the LESS
env var with your other prefs to let anything use those prefs by just running less
).
If you can not get the new version, you might try this instead:
function catless() {
local line buffer='' num=0 limit=$LINES
while IFS='' read -r line; do
buffer="$buffer$line"$'\n'
line=''
num=$(( num+1 ))
[[ $num -ge $limit ]] && break
done
if [[ $num -ge $limit ]]; then
{ printf %s "$buffer$line"; cat } | less
else
printf %s "$buffer$line"
fi
}
The key is that the shell has to know if the there are more lines in the file than the screen before it (potentially) launches less
(the multi-io technique you initially used can only run things in the background). If the in-shell read
is not robust enough for you, you can replace it by reworking the code a bit:
function cat_up_to_N_lines_and_exit_success_if_more() {
# replace this with some other implmentation
# if read -r is not robust enough
local line buffer='' num=0 limit="$1"
while IFS='' read -r line; do
buffer="$buffer$line"$'\n'
line=''
num=$(( num+1 ))
[[ $num -ge $limit ]] && break
done
printf %s "$buffer$line"
[[ $num -ge $limit ]]
}
function catless() {
local limit=$LINES buffer=''
# capture first $limit lines
# the \0 business is to guard the trailing newline
buffer=${"$(
cat_up_to_N_lines_and_exit_success_if_more $limit
ec=$?
printf '\0'
exit $ec)"%$'\0'}
use_pager=$?
if [[ $use_pager -eq 0 ]]; then
{ printf '%s' "$buffer"; cat } | less
else
printf '%s' "$buffer"
fi
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With