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


35

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


1
如果有多条“最长”的线呢?因为您想要的不仅仅是一个简单的最大长度,您是否要查看所有最长的线实例?
Peter.O 2011年

Answers:


39
cat ./text | awk ' { if ( length > x ) { x = length; y = $0 } }END{ print y }'

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

awk 'length > max_length { max_length = length; longest_line = $0 } END { print longest_line }' ./text 

3
cat就是说,调用另一个命令()和使用管道都是昂贵的操作,更不用说awk读取文件更有效。如果经常这样做,对性能的影响绝对是显而易见的,即使这样,您也完全会滥用cat
克里斯·

7
@laebshade绝对有一个原因-这样您就不必记住哪些命令使用文件名,哪些不使用文件名,或担心哪个命令将首先在管道中执行。如果您要编写经常运行的脚本,则一定要担心这样的事情。如果您要编写一次性文件来查找文件中的最长行,则额外的过程和所消耗的时间是完全无关的。人们如此痴迷于这里,真是
太少

4
@基思·汤普森:cat在这里不是没有用的。它可能对计算机没有用,但对于人类读者来说,它可以提供价值。第一个变体清楚地显示了输入。流程更自然(从左到右)。在第二种情况下,除非滚动窗口,否则您不知道输入是什么。
jfs

1
@JFSebastian即使您想要在左侧,也不需要cat< file command效果很好。
克里斯·

3
@JFSebastian:可以在命令开始处写入重定向的事实有点模糊;< filename command等同filename < command于我尝试过的每个shell。但是,一旦意识到这一点,就可以在编写长管道时清楚地显示数据流的方向(而无需调用额外的命令)来利用它:< input-file command1 | command2 | command3 > output-file
Keith Thompson

6
cat filename | awk '{ print length }' | sort -n | tail -1

+1有很多有趣的解决方案,但这是最简单的。(通过让awk读取文件而没有猫会更简单,但是为什么要
仔细检查

5
sed -rn "/.{$(<file expand -t1 |wc -L)}/{p;q}" file

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

另一个,我早于sed的想法(以bash表示):

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

2
该方法非常昂贵且缓慢。
克里斯·

2
@克里斯唐:哦,是的。但是问题是关于最有效的方法,而不是最有效的方法。不过,它适合中小型文件或非关键任务。
阿拉木图

3
警告:wc的选项-L, --max-line-length根据手册页显示最长行的长度,但是如果您深入挖掘(例如,当您获得错误/意外结果时),您会发现此选项将每1个制表符的长度增加8 看到这个Unix和Linux Q / A\x09
Peter.O 2011年

PS。您的答案将打印所有“等长”行,这可能是一件好事...强制wc每个选项卡仅计数1个字符,这可行。sed -rn "/.{$(<file expand -t1 |wc -L)}/p" file
Peter.O 2011年

1
read line会将反斜杠转义的char解释为文字char,例如\Aresloves to A,它当然会报告比实际的字节使用时间短的字符...为了防止这种转义的解释,请使用:read -r line。。。。另外,要使sed + wc版本在第一条“最长的一行”之后退出,请更改p{p;q}..sed -rn "/.{$(<file expand -t1 |wc -L)}/{p;q}" file
Peter.O 2011年

4

这是一个Perl解决方案:

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

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

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

由于没有更好的事情要做,因此我在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

打印所有最长的行:

$ 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

3

Grep第一最长的线

grep -Em1 "^.{$(wc -L <file.txt)}\$" file.txt 

该命令通常不经过实践便很难阅读,因为它混合了shell和regexp语法。
为了说明,我将首先使用简化的伪代码。以开头的行##不在外壳中运行。
此简化的代码使用文件名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"

显示第一条最长的线 -第一条与最长的线一样长的线:

grep -E -m1 "^.{$(wc -L <"$f")}\$" "$f"

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

grep -E "^.{$(wc -L <"$f")}\$" "$f" 

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

tac "$f" | grep -E -m1 "^.{$(wc -L <"$f")}\$"

显示一条最长的线 -最长的线比所有其他线长,否则失败:

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

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


很棒的答案,从中学到了很多。谢谢
somethingSomething

2

以下示例本来是并且应该是对dmitry.malikov的答案的评论,但是由于 那里有可见评论空间无用使用,因此我选择在此处展示它,至少可以在此处看到它。 ..

这是德米特里 单遍awk方法的简单变体。
它打印所有“等长”行。(注意。delete array是gawk扩展名)。

awk 'length >x { delete y; x=length }
     length==x { y[NR]=$0 } END{ for (z in y) print y[z] }' file

1

用纯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[@]}"

照原样,该代码可以返回无效结果。设置_max_line[0]=${_line}不会删除任何之前累积的较短“最长行”的其余部分... unset _max_line将清除整个阵列...
Peter.O 2011年

@fered谢谢,写得很快。固定。
克里斯·

0

我为此开发了一个小shell脚本。它显示的长度,行号和行本身超过特定大小(例如80个字符)的长度:

#!/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


1
您可以进行一些改进。引用变量。这将破坏包含空格或其他奇怪字符的任何文件名。您想要使用$*很少是一个好主意。在你自认为空行相匹配,以及没有做任何事情。如果您单引号,则可以避免转义。为什么要使用空块?最后,您不需要,只需"$@"/.*/awk\$0'EOF'BEGIN{}catawk . . . "$file" | . . .
terdon

1
您也可以直接在awk中直接完成整个操作:awk -vmax=15 '{len=length($0); if(len>=max){printf("%s, %d at line # %d %s\n", FILENAME, len, NR, $0);}}' file*
terdon

-3

您可以使用wc

wc -L fileName

3
请再次阅读问题。所需的输出是最长的线本身,而不是最长的线的长度。另请参阅Peter.O关于wc -L弊端的评论
manatwork
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.