I'm looking for the simplest method to print the longest line in a file. I did some googling and surprisingly couldn't seem to find an answer. I frequently print the length of the longest line in a file, but I don't know how to actually print the longest line. Can anyone provide a solution to print the longest line in a file? Thanks in advance.
-
1What about when there are multiple "longest" lines?. Because you want more than a simple maximum length, do you want to see all instances of lines which are equal longest?– Peter.OCommented Nov 13, 2011 at 14:20
-
related: stackoverflow.com/questions/1655372/longest-line-in-a-file– Ciro Santilli OurBigBook.comCommented Nov 3, 2023 at 18:12
13 Answers
cat ./text | awk ' { if ( length > x ) { x = length; y = $0 } }END{ print y }'
UPD: summarizing all the advices in the comments
awk 'length > max_length { max_length = length; longest_line = $0 } END { print longest_line }' ./text
-
3It is, both calling another command (
cat
), and using a pipe are expensive operations, not to mention that it's more efficient for awk to just read the file. The performance implications are definitely noticeable if this is done frequently, and even so, you are completely misusingcat
. Commented Nov 13, 2011 at 2:37 -
10@laebshade There absolutely is a reason -- it's so you don't need to remember which commands take filenames and which don't, or care about which command is going to execute first in the pipeline. If you're going to be writing a script that gets run frequently, by all means worry about something like this. If you're writing a one-off thing to find the longest line in a file, the extra process and fractional amount of time consumed is completely irrelevant. It's silly that people are so obsessed with it here, it's incredibly minor Commented Nov 13, 2011 at 17:41
-
5@Keith Thompson:
cat
is not useless here. It might be useless to a computer but for a human reader it could provide value. The first variant clearly shows the input. The flow is more natural (from left to right). In the second case you don't know what the input is unless you scroll the window.– jfsCommented Nov 14, 2011 at 3:02 -
2@J.F.Sebastian Even if you want it at the left, you don't need
cat
.< file command
works just fine. Commented Nov 14, 2011 at 3:03 -
5@J.F.Sebastian: The fact that a redirection can be written at the beginning of a command is somewhat obscure;
< filename command
is equivalent tofilename < command
in every shell I've tried. But once you're aware of it, you can take advantage of it when writing long pipes that clearly show the direction of the data flow (without invoking an extra command):< input-file command1 | command2 | command3 > output-file
Commented Nov 14, 2011 at 3:16
cat filename | awk '{ print length }' | sort -n | tail -1
-
+1 There were lots of interesting solutions to this but this was the simplest. (It would be simpler without the cat by letting awk read the file but why quibble?) Commented Apr 19, 2019 at 15:14
-
This I can rememeber. ДМИТРИЙ МАЛИКОВ answer is probably most efficent though Commented May 12, 2020 at 15:55
-
3
-
3As I just learned from Volker Siegel's answer, this whole expensive command can be replaced by a simple
wc -L filename
. And @ChrisWue is correct - but I stumbled upon this whole question trying to find the LENGTH of the longest line, and this answered it :-)– WolframCommented Oct 30, 2020 at 17:51 -
To also print the line, change the stuff after
cat filename
to| awk '{ print length " " $0 }' | sort -n | tail -1
– user114651Commented Mar 13, 2021 at 5:16
Grep the first longest line
grep -Em1 "^.{$(wc -L <file.txt)}\$" file.txt
The command is unusually hard to read without practise because it mixes shell- and regexp syntax.
For explanation, I will use simplified pseudocode first. The lines starting with ##
do not run in the shell.
This simplified code uses the file name F, and leaves out quoting and parts of regexps for readability.
How it works
The command has two parts, a grep
- and a wc
invocation:
## grep "^.{$( wc -L F )}$" F
The wc
is used in a process expansion, $( ... )
, so it is run before grep
. It calculates the length of the longest line. The shell expansion syntax is mixed with the regular expression pattern syntax in a confusing way, so I will decompose the process expansion:
## wc -L F
42
## grep "^.{42}$" F
Here, the process expansion was replaced with the value it would return, creating the grep
commandline that is used. We can now read the regular expression more easily: It matches exactly from start (^
) to end ($
) of the line. The expression between them matches any character except newline, repeated by 42 times. Combined, that is lines that consist of exactly 42 characters.
Now, back to real shell commands: The grep
option -E
(--extended-regexp
) allows to not escape the {}
for readability. Option -m 1
(--max-count=1
) makes it stop after the first line is found. The <
in the wc
command writes the file to its stdin, to prevent wc
from printing the file name together with the length.
Which longest lines?
To make the examples more readable with the filename occurring twice, I will use a variable f
for the filename; Each $f
in the example could be replaced by the file name.
f="file.txt"
Show the first longest line - the first line that is as long as the longest line:
grep -E -m1 "^.{$(wc -L <"$f")}\$" "$f"
Show all longest lines - all lines that are as long as the longest line:
grep -E "^.{$(wc -L <"$f")}\$" "$f"
Show the last longest line - the last line that is as long as the longest line:
tac "$f" | grep -E -m1 "^.{$(wc -L <"$f")}\$"
Show the single longest line - the longest line longer than all other lines, or fail:
[ $(grep -E "^.{$(wc -L <"$f")}\$" "$f" | wc -l) = 1 ] && grep -E "^.{$(wc -L <"$f")}\$" "$f"
(The last command is even more inefficient than the others, as it repeats the complete grep command. It should obviously be decomposed so that the output of wc
and the lines written by grep
are saved to variables.
Note that all longest lines may actually be all lines. For saving in a variable, only the first two lines need to be kept.)
-
-
Interesting approach but doesn't work for long lines (
grep: regular expression too big
) (I have some files with lines with lengths of around 760kb)– ChrisWueCommented Aug 3, 2020 at 22:12
Here's a Perl solution:
perl -e 'while(<>){
$l=length;
$l>$m && do {$c=$_; $m=$l}
} print $c' file.txt
Or, if you want to print all the longest lines
perl -e 'while(<>){
$l=length;
push @{$k{$l}},$_;
$m=$l if $l>$m;
} print @{$k{$m}}' file.txt
Since I had nothing better to do, I ran some benchmarks on a 625M text file. Surprisingly, my Perl solution was consistently faster than the others. Granted, the difference with the accepted awk
solution is tiny, but it is there. Obviously, solutions that print multiple lines are slower so I have sorted by type, fastest to slowest.
Print only one of the longest lines:
$ time perl -e 'while(<>){
$l=length;
$l>$m && do {$c=$_; $m=$l}
} print $c' file.txt
real 0m3.837s
user 0m3.724s
sys 0m0.096s
$ time awk 'length > max_length { max_length = length; longest_line = $0 }
END { print longest_line }' file.txt
real 0m5.835s
user 0m5.604s
sys 0m0.204s
$ time sed -rn "/.{$(<file.txt expand -t1 |wc -L)}/{p;q}" file.txt
real 2m37.348s
user 2m39.990s
sys 0m1.868s
Print all longest lines :
$ time perl -e 'while(<>){
$l=length;
push @{$k{$l}},$_;
$m=$l if $l>$m;
} print @{$k{$m}}' file.txt
real 0m9.263s
user 0m8.417s
sys 0m0.760s
$ time awk 'length >x { delete y; x=length }
length==x { y[NR]=$0 } END{ for (z in y) print y[z] }' file.txt
real 0m10.220s
user 0m9.925s
sys 0m0.252s
## This is Chris Down's bash solution
$ time ./a.sh < file.txt
Max line length: 254
Lines matched with that length: 2
real 8m36.975s
user 8m17.495s
sys 0m17.153s
sed -rn "/.{$(<file expand -t1 |wc -L)}/{p;q}" file
This first reads the file inside the command substitution and outputs the length of the longest line, (previously, expand
converts tabs to spaces, to overcome the semantics of wc -L
-- each tab in the line will add 8 instead of 1 to line length). This length is then used in a sed
expression meaning "find a line this number of characters long, print it, then quit". So this actually can be as optimal as the longest line is near to the top of the file, heheh (thanks fered for the awesome and constructive comments).
Another, I had thought earlier than the sed one (in bash):
#!/bin/bash
while read -r line; do
(( ${#line} > max )) && max=${#line} && longest="$line"
done
echo "$longest"
-
2
-
2@Chris Down: Oh yes it is. But the question was about the sortest method, not the most efficient. WOrks finely for small to medium files or non critical tasks, though.– ataCommented Nov 13, 2011 at 11:49
-
4WARNING: wc's option
-L, --max-line-length
prints the length of the longest line, according to the man page, but if you dig deeper (as in when you get wrong / unexpected results), you find that this option increments the length by 8 for each 1 tab char\x09
see this Unix & Linux Q/A– Peter.OCommented Nov 13, 2011 at 13:05 -
PS. Your answer will print all the "equally longest" lines, which is probably a good thing... To force wc to count only 1 char per tab, this works.
sed -rn "/.{$(<file expand -t1 |wc -L)}/p" file
– Peter.OCommented Nov 13, 2011 at 14:13 -
1
read line
will interpret backslash-escaped chars as the literal char, eg\A
resloves toA
, which of course effectively reports a shorter than actual byte-usage... To prevent this escaped interpretation, use:read -r line
. . . . Also, to make the sed+wc version quit after the first "longest line", changep
to{p;q}
..sed -rn "/.{$(<file expand -t1 |wc -L)}/{p;q}" file
– Peter.OCommented Nov 14, 2011 at 3:15
The following example was going to be, and should have been, a comment to dmitry.malikov's answer, but because of the Useless Use of Visible Comment Space there, I've chosen to present it here, where it will at least be seen...
This is a simple variation of the dmitry's
single-pass awk method.
It prints all "equal longest" lines. (Note. delete array
is a gawk extension).
awk 'length >x { delete y; x=length }
length==x { y[NR]=$0 } END{ for (z in y) print y[z] }' file
In pure bash:
#!/bin/bash
_max_length=0
while IFS= read -r _line; do
_length="${#_line}"
if (( _length > _max_length )); then
_max_length=${_length}
_max_line=( "${_line}" )
elif (( _length == _max_length )); then
_max_line+=( "${_line}" )
fi
done
printf 'Max line length: %d\n' "${_max_length}"
printf 'Lines matched with that length: %d\n' "${#_max_line[@]}"
(( ${#_max_line[@]} )) && printf '%s\n' '----------------' "${_max_line[@]}"
-
As-is, the code can return invalid results. Setting
_max_line[0]=${_line}
does not remove the rest of any previously accumulated shorter "longest lines"...unset _max_line
will clear the entire array...– Peter.OCommented Nov 13, 2011 at 21:00 -
@fered Thanks for that, was written pretty quickly. Fixed. Commented Nov 13, 2011 at 21:03
awk '{ print length(), $0 | "sort -n" }' file.txt | tail -1
Reference: https://www.systutorials.com/how-to-sort-lines-by-length-in-linux/
I have developed a small shell script for this. It displays length, line # and line itself by length that exceeds a particular size like 80 characters:
#!/bin/sh
# Author: Surinder
if test $# -lt 2
then
echo "usage: $0 length file1 file2 ..."
echo "usage: $0 80 hello.c"
exit 1
fi
length=$1
shift
LONGLINE=/tmp/longest-line-$$.awk
cat << EOF > $LONGLINE
BEGIN {
}
/.*/ {
current_length=length(\$0);
if (current_length >= expected_length) {
printf("%d at line # %d %s\n", current_length, NR, \$0);
}
}
END {
}
EOF
for file in $*
do
echo "$file"
cat $file | awk -v expected_length=$length -f $LONGLINE |sort -nr
done
rm $LONGLINE
https://github.com/lordofrain/tools/blob/master/longest-line/longest-line.sh
-
1There are a few improvements you could make. Quote your variables. This will break on any file names that contain whitespace or other strange characters. Using
$*
is rarely a good idea, you want"$@"
. The/.*/
in yourawk
doesn't do anything since that matches empty lines as well. You could avoid escaping the\$0
if you single quote the'EOF'
. Why use an emptyBEGIN{}
block? Finally, you don't needcat
, justawk . . . "$file" | . . .
– terdon ♦Commented Oct 19, 2016 at 9:24 -
1You couuld also just do the whole thing in awk directly:
awk -vmax=15 '{len=length($0); if(len>=max){printf("%s, %d at line # %d %s\n", FILENAME, len, NR, $0);}}' file*
– terdon ♦Commented Oct 19, 2016 at 9:25
This is a solution using python
python -c 'import sys;print(max(open(sys.argv[1],"r").readlines(), key=len))' file.txt
If you counting graphemes (user perceived character) in a Unicode file rather than ASCII bytes, you can use Perl:
perl -CSD -lnE '$l=()=/\X/g;
if ($l>$m) {$max=$_; $m=$l}
END{say "$max"}' your_file
Or this Ruby:
ruby -le 'ml=$<.max_by{|ln| ln.scan(/\X/).length}
END{puts "#{ml}" }' your_file
(edit of code above in @ДМИТРИЙ МАЛИКОВ (Dmitry Malikov)'s popular post from 2011-11-13:)
this prints out the line number, the length, and underlines the contents of only the first longest line:
awk 'length>len{len=length;line=FNR;long=$0}END{print"line="line" len="len" long=\033[4m\n"long"\033[0m"}'
<"${filename}"
(underlining seemed best because the text might contain color sequences or white space.)
also, to strip out non-printing characters (if desired), instead of just <"${filename}"
,
you could use this very concise "ansifilter" alternative found
here:
<(sed "s,\x1b\[[0-9;]*[a-zA-Z],,g" "${filename}"|expand)
(or check out the real ansifilter for serious projects--thanks to whoever mentioned it before me!)
-
Hi @AnonyMuse00yahoo, I declined your flag because I don't understand what you mean. If you want to propose an edit to Dmitry Malikov's answer, go ahead! If you want to post a new answer yourself, you can also do this. This answer is deleted (it can't be any more deleted than this). You can still see it because it's yours, but other users won't (unless they have enough reputation points to see deleted posts).– terdon ♦Commented Jul 15, 2021 at 16:08
-
I also undeleted this answer, at least temporarily, so that you can respond to my comment. Just include
@terdon
and I will be notified and we can sort this out.– terdon ♦Commented Jul 15, 2021 at 16:09 -
@terdon thanks! but it seems that even with this answer deleted, i cannot re-answer the question anew. is it only possible to ever have one answer per user per question? i guess i am trying to start over on this question with a blank slate, now that i actually understand how edits and revisions work. i am kind of a perfectionist. i also understand the original question better and have better ideas. Commented Jul 19, 2021 at 2:45
-
I don't know of any reason you wouldn't be able to post. The system does give you a popup asking if you are sure, could you be stopping at that? If not, you can simply click on the "add another answer" or, if this is deleted "post your answer" and you should be fine. What do you actually see?– terdon ♦Commented Jul 19, 2021 at 10:55
You can use wc
:
wc -L fileName
-
3Please read the question again. The required output is the longest line itself, not the length of the longest line. Also see Peter.O's comment regarding
wc -L
's drawback. Commented May 1, 2013 at 11:47 -
1For the xy problem that I had, this was helpful. It does not answer the question in the OP, but it is relevant tangential information and I'm happy that it was here. Commented Oct 21, 2021 at 10:11