Suppose your tput cols
(or COLUMNS
) is equal to 100
and you have a plain text file foo.txt
with a single line that is 120 characters long.
If you wanted to count number of lines it contains you could do cat foo.txt | wc -l
and unsurprisingly enough the output would presumably be 1
.
But if you’d open the file with a pager, such as less
, like less foo.txt
then what your eyes would actually see are two lines instead (AFAIK, unless you don’t say --chop-long-lines
, less
will “wrap" lines that are longer than your terminal’s width).
Again, if you’d try to see line numbers, using less
, like less --LINE-NUMBERS foo.txt
, than the output would be something like:
1 something something...
1 more stuff
Basically less
is “aware” that the only line in foo.txt
is longer than your terminal’s width, so it will “wrap” it to visualize, but will tell you that actually first and the second lines that you see are actually the same line #1 in foo.txt
.
So, my question is: how could you “calculate” (say, in bash) number of lines after wrapping (number of lines that your eyes see), rather than number of lines that the file actually contains? (In scenario above, the number would be 2
instead of 1
.)
Actually, there is a better solution:
fold -w "$COLUMNS" testfile | wc -l
The fold
command will wrap a file to a given column count and is widely available as part of the GNU coreutils.
This solution will print the number of lines as displayed on the terminal:
#!/bin/bash
seenlines=0
cols=$( tput cols )
# iterate over each line in the file:
while read line
do
# get length of the line in characters,
# subtracting the newline:
length=$(( $( wc -m <<< "$line" ) - 1 ))
# add at least one line, and one for each time
# the line length exceeds the column size of
# the terminal, subtracting one character to
# avoid the edge case of length==cols:
seenlines=$( bc <<< "$seenlines + 1 + (${length}-1)/ ${cols}" )
done <testfile
echo "${seenlines} lines seen"
Note: $COLUMNS is built in to bash and returns the current number of columns available for display.
We need to count the number of lines above our $COLUMN limit. We can do this with grep:
expression='.\{'"$COLUMNS"'\}'
echo $expression
.\{80\}
cat foo.txt | grep -c $expression
1
If we add this number to cat foo.txt | wc -l
we get the number of lines after wrapping.
Script:
#!/bin/bash
eval $(resize)
expression='.\{'"$COLUMNS"'\}'
echo $expression
raw_lines=`cat foo.txt | wc -l`
big_lines=`cat foo.txt | grep -c "$expression"`
display_lines=`echo "$big_lines + $raw_lines" | bc`
echo "raw lines: $raw_lines"
echo "big lines: $big_lines"
echo "display lines: $display_lines"
Note: line 2 eval $( resize )
is needed to make the $COULMNS variable available from within the script.
Hope this does the trick for you!
Just for kicks, here is how you could find the minimum possible lines after wrapping (assuming no line breaks).
characters="$(cat foo.txt | wc -c)"
minimum_possible_lines_after_wrapping="$(echo $COLUMNS | xargs echo "$characters / " | bc)"
- cat the file
- count the characters
- divide the number of characters by the maximum possible on each line
This only gets us the minimum possible number of lines, however.
来源:https://stackoverflow.com/questions/29996142/count-number-of-lines-after-wrapped-by-terminal-s-length