How to make less paginate only when the input is larger than the screen size? [duplicate] - linux

This question already has answers here:
Count number of lines after wrapped by terminal’s length [closed]
(3 answers)
Closed 5 years ago.
If I want to clear the screen and run less if the output of a program exceeds the number of lines on a screen, I can do something like:
mypgm | tee mypgm.out
LEN=$(wc -l mypgm.out | cut -f1 -d' ')
[[ $LEN >= $LINES ]]; then
clear
less -R mypgm.out
fi
It works just fine as long as none of the lines in the output wrap around, visually taking up more than a single line on the screen.
Is there an simple way to determine how many lines of the screen the output occupies visually, or do I have to loop over the output file, computing the number of "lines" a long line needs to display by using $COLUMNS

Seems like you're actually looking for something like:
mycmd | less -F
From man less:
-F or --quit-if-one-screen
Causes less to automatically exit if the entire file can be displayed on the first screen.
You can make it a default behavior with:
export LESS='-F'
in your .bashrc.
The -F option is nicely combined with -X which will skip clearing of the screen before listing (can also have it as a default with LESS='-FX').

I found fold, which solves the problem:
fold -w "$COLUMNS" mypgm.out | wc -l
(no need for the 'cut' since wc is running against a pipe)

Related

How to overwrite previous output in bash

I have a bash script, that outputs top most CPU intensive processes every second to the terminal.
tmp=$(ps -e -eo pid,cmd,%mem,%cpu,user --sort=-%cpu | head -n 11)
printf "\n%s\n" "$tmp[pid]"
I know that I can move my cursor to the predeclared position, but that fails every time terminal is not cleared.
I could also just go to the beginning of the line and write over it, but that again makes a problem when current output is shorter that the previous and when the number of lines is not the same as it was at the previous output.
Is there a way to completely erase the previous output and write from there?
Yes, you can clear a part of the screen before each iteration (see https://unix.stackexchange.com/questions/297502/clear-half-of-the-screen-from-the-command-line), but the function watch does it for you. Try:
watch -n 1 "ps -e -eo pid,cmd,%mem,%cpu,user --sort=-%cpu | head -n 11"

Different behaviour in bash when printing output from command compared to hardcoded string

I am trying to use tmux and simple bash scripts to get a very basic statusbar in my terminals - so the windows are very small (1 or 2 lines in height and potentially less than 10 characters in width).
The command that's causing a strange behaviour is:
echo "$(top -bn 1 | awk '/^%Cpu/{print $2}')%"
which is basically just supposed to print out something like 1.8%.
In a normal sized terminal (with or without tmux) it works fine but when I shrink down the terminal, at some point it starts to cut off the number and only displays %. This might not seem too strange but when I put some text around this command, like
echo "ab$(top -bn 1 | awk '/^%Cpu/{print $2}'cd"
I get abcd as output when shrinking the terminal. So it's only affecting the part withing $(...) that gets cut away.
Also when I print two lines:
echo "$(top -bn 1 | awk '/^%Cpu/{print $2}'%"
echo "20.5%"
it will print 20.5% fine, even break it up over several lines like
2
0
.
5
%
rather than just not printing parts of it. The first line however only shows % without any number.
Any idea what is causing this and how to get the numbers to show?
By default, top checks the terminal size and adjusts its output based on it, even when used in batch mode and output is being piped instead of going to the terminal. You can use the -w option to override this.
echo "$(top -w 50 -bn 1 | awk '/^%Cpu/{print $2}')%"

Bash shell script update and print a variable overwriting the same line [duplicate]

This question already has answers here:
Displaying only single most recent line of a command's output
(2 answers)
Closed 5 years ago.
I been trying to print a variable in the same time for a scrip that pretends automatize a process the content is the output of this
sed "s/Read/\n/g" /tmp/Air/test.txt | tail -1 test.txt | grep ARP
so i put this in a while loop
do
out= sed "s/Read/\n/g" /tmp/Air/test.txt | tail -1 test.txt | grep ARP
echo -n "$out"
sleep 1
done
i read other questions here and i try with different option like echo -ne, echo -ne "$out" \r, printf "\r" or printf "%s" and no luck with no one, all the other example don't have a variable to print just counter o system variables
Update
it seems to appear that the echo -n repeat $out in the same line, if out="this is a test" the output of echo -n is "this is a test this is a test this is a test this is a test ...." maybe im missing some option ?
Update 2
sorry for the miss understood perhaps i was not very clear but what i want is overwrite the same line with the value of $out, the source of $out is the output of the aireplay-ng command that executes along with the script and i get the output with
the ouput is something like this
102415 packets (got 5 ARP requests and 15438 ACKs), sent 37085 packets...(499 pps)
but the number of ARP request is changing constantly
this code for example use echo -ne and overwrite in the same line
#!/bin/bash
for pc in $(seq 1 100); do
echo -ne "$pc%\033[0K\r"
sleep 1
done
the output of this is like a percent indicator that shows "10%" and going instead of "1% 2% 3% 4% 5% .." in the same line and i already try like this but with no luck
if you are trying to execute the sed Please use
`sed "s/Read/\n/g" /tmp/Air/test.txt | tail -1 test.txt | grep ARP`
First of all you are assigning a value of bash command wrongly to variable.
out=$(sed "s/Read/\n/g" /tmp/Air/test.txt | tail -1 test.txt | grep ARP)
Then you can print all your output in one line as you wrote:-
echo -n $out
The recent addendum to your question reads like you're miscommunicating your intent: this is a test this is a test this is a test is what a plain reading of your question indicates you to be asking for (printing this is a test over and over in a loop without newlines, after all, can be expected to do nothing else); why you'd describe this in a context that makes it sound like a bug is thus surprising.
If you want to send the cursor back to the beginning of your current line and overwrite that line, that might be something like the following:
#!/bin/bash
# ^^^^ not /bin/sh; this enables bash extensions
# ask the shell to keep $COLUMNS up-to-date
shopt -s checkwinsize
# defaults to 80-character terminal width, but uses $COLUMNS if available
printf "%-${COLUMNS:-80}s\r" "$out"`
...which prints your string, pads out to 80 characters with spaces, and then returns the cursor to the beginning of the line, such that the next thing you write will overwrite that string.
Of course, if you print that line and then return to a shell prompt, the prompt will start at the beginning of the same line and overwrite the text, so be sure to follow up with an echo.

How to extract certain lines from command history in linux

I am trying to count the all processes opened by all commands which contain the word valyria. I thought about this solution:
writing a one-liner, such as number=$(history | grep "valyria" | wc -l) && echo $number, but this does not seem to print anything(the variable has been previously declared).
My other question is this: if I enter a command which contains the word valyrian, will that command open only 1 proceess or more? If the later is true, I guess that my one-liner becomes useless, since it counts 1 match=1 process. How could I achieve the desired result?
Thanks, Polb

Continuous grep, output at same spot on console

I use
tail -f file | grep pattern
all the time for continuous grep.
However, is there a way I can make grep output its pattern at the same spot, say at the top of the screen? so that the screen doesn't scroll all the time?
My case is something like this: tail -f log_file | grep Status -A 2 will show the current status and what changed it to that status. The problem is the screen scrolls and it becomes annoying. I'd rather have the output stuck on the first 3 lines in the screen.
Thank you!
you could use the watch command; which will always execute the same command, but the position on the screen will stay the same. The process might eat some more CPU or memory though:
watch "tail file | grep pattern"
by default watch executes that command every 2 seconds. You can adjust up to 0.1 seconds using:
watch -n 0.1
NOTE
As noted by #etanReisner: this is not exactly the same as tail -f: tail -f will change immediately if something is added to your logfile, the watch command will only notice that when it executes, ie every 2 (or 0.1 seconds).
Assuming you are using a vt100 compatible emulator...
This command will tail a file, pipe it into grep, read it a line at a time and then display it in reverse on the top line of the screen:
TOSL=$(tput sc;tput cup 0 0;tput rev;tput el)
FROMSL=$(tput sgr0; tput rc)
tail -f file | grep --line-buffered pattern | while read line
do
echo -n "$TOSL${line}$FROMSL"
done
It assumes your output appears a line at a time. If you want more than one line, you can read more than a line, but you have to decide how you want to buffer the output. You could also use the csr terminfo command to set up an entire separate scrolling region instead of just having one line.
Here is the scrolling region version with a ten line status area at the top:
TOSL=$(tput sc; tput csr 0 10; tput cup 10 0;tput rev;tput el)
FROMSL=$(tput sgr0; tput rc;tput csr 10 50;tput rc)
tail -f file | grep --line-buffered pattern | while read line
do
echo -n "$TOSL${line}
$FROMSL"
done
Note that it is not impossible that your display will be corrupted from time-to-time as it could be that the output from your main shell and your background task get mixed up.
Simply replace the newlines with carriage returns.
tail -f file | grep --line-buffered whatever | tr '\012' '\015'
The line buffering is to avoid jumpy output; see http://mywiki.wooledge.org/BashFAQ/009
This is quick and dirty. As noted in comments, this will leave the previous contents of the line underneath, so a shorter line will not completely overlay a longer line. You could add some control codes to address that, but then you might as well use Curses for the formatting too, like in rghome's answer.

Resources