如何打印文件中最长的一行?

dr.*_*sen 57 bash awk filter

我正在寻找最简单的方法来打印文件中最长的行。我做了一些谷歌搜索,令人惊讶的是似乎找不到答案。我经常打印文件中最长行的长度,但我不知道如何实际打印最长行。谁能提供打印文件中最长行的解决方案?提前致谢。

ДМИ*_*КОВ 64

cat ./text | awk ' { if ( length > x ) { x = length; y = $0 } }END{ print y }'
Run Code Online (Sandbox Code Playgroud)

UPD:总结评论中的所有建议

awk 'length > max_length { max_length = length; longest_line = $0 } END { print longest_line }' ./text 
Run Code Online (Sandbox Code Playgroud)

  • @laebshade 这绝对是有原因的——这样你就不需要记住哪些命令需要文件名,哪些不需要,或者关心哪个命令将首先在管道中执行。如果您要编写一个经常运行的脚本,请务必担心这样的事情。如果您正在编写一次性的东西来查找文件中最长的行,那么额外的过程和消耗的部分时间完全无关紧要。人们对这里如此痴迷,这太愚蠢了,它太小了 (8认同)
  • @JFSebastian:可以在命令的开头写入重定向的事实有些模糊;在我尝试过的每个 shell 中,`< filename command` 等价于 `filename < command`。但是一旦你意识到这一点,你就可以在编写清晰显示数据流方向的长管道时利用它(无需调用额外的命令):`< input-file command1 | 命令 2 | 命令 3 > 输出文件` (5认同)
  • @Keith Thompson:`cat` 在这里不是没用。它可能对计算机无用,但对于人类读者来说,它可以提供价值。第一个变体清楚地显示了输入。流动更自然(从左到右)。在第二种情况下,除非滚动窗口,否则您不知道输入是什么。 (4认同)
  • 也就是说,调用另一个命令(`cat`)和使用管道都是昂贵的操作,更不用说 awk 只读取文件的效率更高了。如果经常这样做,性能影响肯定会很明显,即使如此,你也完全误用了 `cat`。 (3认同)
  • @JFSebastian 即使你想在左边,你也不需要`cat`。`< 文件命令` 工作得很好。 (2认同)

小智 14

cat filename | awk '{ print length }' | sort -n | tail -1
Run Code Online (Sandbox Code Playgroud)

  • Nit:这只会打印长度而不是线本身 (2认同)
  • 正如我刚刚从 Volker Siegel 的回答中了解到的,这整个昂贵的命令可以替换为一个简单的 `wc -L filename`。@ChrisWue 是正确的 - 但我偶然发现了整个问题,试图找到最长线的长度,这回答了它:-) (2认同)

Vol*_*gel 12

Grep 第一个最长的行

grep -Em1 "^.{$(wc -L <file.txt)}\$" file.txt 
Run Code Online (Sandbox Code Playgroud)

该命令在没有练习的情况下非常难以阅读,因为它混合了 shell 和 regexp 语法。
为了说明,我将首先使用简化的伪代码。以开头的行##不在 shell 中运行。
这个简化的代码使用文件名 F,为了可读性,省略了引用和部分正则表达式。

这个怎么运作

该命令有两部分,一个grep- 和一个wc调用:

## grep "^.{$( wc -L F )}$" F

wc是在以下过程中使用的扩展,$( ... ),所以它之前运行grep。它计算最长线的长度。shell 扩展语法与正则表达式模式语法混合在一起很混乱,所以我将分解过程扩展:

## wc -L F
42
## grep "^.{42}$" F

在这里,进程扩展被替换为它将返回的值,从而创建了grep所使用的命令行。我们现在可以更轻松地阅读正则表达式:它从行的开始 ( ^) 到结束 ( $)完全匹配。它们之间的表达式匹配除换行符之外的任何字符,重复 42 次。组合起来,即正好由 42 个字符组成的行。


现在,回到真正的 shell 命令:grep选项-E( --extended-regexp) 允许不转义{}以提高可读性。选项-m 1( --max-count=1) 使其在找到第一行后停止。的<wc命令中的文件写入其标准输入,以防止wc从与长度一起打印的文件名。

哪条线最长?

为了使示例在文件名出现两次时更具可读性,我将为f文件名使用一个变量;$f示例中的每个都可以替换为文件名。

f="file.txt"
Run Code Online (Sandbox Code Playgroud)

显示第一个最长的行- 与最长的行一样长的第一行:

grep -E -m1 "^.{$(wc -L <"$f")}\$" "$f"
Run Code Online (Sandbox Code Playgroud)

显示所有最长的行- 所有与最长行一样长的行:

grep -E "^.{$(wc -L <"$f")}\$" "$f" 
Run Code Online (Sandbox Code Playgroud)

显示最后最长的行- 与最长行一样长的最后一行:

tac "$f" | grep -E -m1 "^.{$(wc -L <"$f")}\$"
Run Code Online (Sandbox Code Playgroud)

显示单个最长的行- 最长的行比所有其他行都长,否则失败:

[ $(grep -E "^.{$(wc -L <"$f")}\$" "$f" | wc -l) = 1 ] && grep -E "^.{$(wc -L <"$f")}\$" "$f" 
Run Code Online (Sandbox Code Playgroud)

(最后一个命令比其他命令效率更低,因为它重复了完整的 grep 命令。显然应该将其分解,以便将 的输出wc和写入的行grep保存到变量中。
注意,所有最长的行实际上可能是所有行.为了保存在变量中,只需要保留前两行。)


ata*_*ata 6

sed -rn "/.{$(<file expand -t1 |wc -L)}/{p;q}" file
Run Code Online (Sandbox Code Playgroud)

这首先读取命令替换内的文件并输出最长行的长度(以前,expand将制表符转换为空格,以克服语义wc -L- 行中的每个制表符都会将行长度加8 而不是 1)。然后在sed表达式中使用这个长度,意思是“找到一行这个长度的字符,打印它,然后退出”。所以这实际上可以是最佳的,因为最长的行靠近文件的顶部,呵呵(感谢提供令人敬畏和建设性的评论)。

另一个,我在 sed 之前就想到了(在 bash 中):

#!/bin/bash
while read -r line; do
    (( ${#line} > max )) && max=${#line} && longest="$line"
done
echo "$longest"
Run Code Online (Sandbox Code Playgroud)

  • *警告*:wc 的选项`-L, --max-line-length` 根据手册页打印最长行的长度,但如果你深入挖掘(比如当你得到 ** 错误/意外**结果),你会发现这个选项为每个 **1** tab char `\x09` 增加了 **8** 的长度,参见这个 [Unix &amp; Linux Q/A](http://unix.stackexchange.com /questions/20551/wc-l-reports-a-line-length-of-8-for-a-tab-char-bug-or-feature) (4认同)
  • 这种方法非常昂贵且缓慢。 (2认同)
  • @Chris Down:哦,是的。但问题是关于最排序的方法,而不是最有效的方法。不过,适用于中小型文件或非关键任务。 (2认同)

ter*_*don 5

这是一个 Perl 解决方案:

perl -e 'while(<>){
           $l=length;  
           $l>$m && do {$c=$_; $m=$l}  
         } print $c' file.txt 
Run Code Online (Sandbox Code Playgroud)

或者,如果您想打印所有最长的行

perl -e 'while(<>){
           $l=length;
           push @{$k{$l}},$_;
           $m=$l if $l>$m;
         } print @{$k{$m}}' file.txt 
Run Code Online (Sandbox Code Playgroud)

由于我无事可做,我在 625M 文本文件上运行了一些基准测试。令人惊讶的是,我的 Perl 解决方案始终比其他解决方案快。诚然,与公认的awk解决方案的差异很小,但确实存在。显然,打印多行的解决方案速度较慢,所以我按类型排序,从最快到最慢。

只打印最长的一行:

$ 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
Run Code Online (Sandbox Code Playgroud)

打印所有最长的行:

$ 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
Run Code Online (Sandbox Code Playgroud)