Answers:
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
cat
就是说,调用另一个命令()和使用管道都是昂贵的操作,更不用说awk读取文件更有效。如果经常这样做,对性能的影响绝对是显而易见的,即使这样,您也完全会滥用cat
。
cat
在这里不是没有用的。它可能对计算机没有用,但对于人类读者来说,它可以提供价值。第一个变体清楚地显示了输入。流程更自然(从左到右)。在第二种情况下,除非滚动窗口,否则您不知道输入是什么。
cat
。< file command
效果很好。
< filename command
等同filename < command
于我尝试过的每个shell。但是,一旦意识到这一点,就可以在编写长管道时清楚地显示数据流的方向(而无需调用额外的命令)来利用它:< input-file command1 | command2 | command3 > output-file
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"
-L, --max-line-length
根据手册页显示最长行的长度,但是如果您深入挖掘(例如,当您获得错误/意外结果时),您会发现此选项将每1个制表符的长度增加8 看到这个Unix和Linux Q / A\x09
sed -rn "/.{$(<file expand -t1 |wc -L)}/p" file
read line
会将反斜杠转义的char解释为文字char,例如\A
resloves to A
,它当然会报告比实际的字节使用时间短的字符...为了防止这种转义的解释,请使用:read -r line
。。。。另外,要使sed + wc版本在第一条“最长的一行”之后退出,请更改p
为{p;q}
..sed -rn "/.{$(<file expand -t1 |wc -L)}/{p;q}" file
这是一个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
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
保存到变量中。
请注意,所有最长的行实际上可能是所有行。为了保存变量,只需要保留前两行。)
用纯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
将清除整个阵列...
我为此开发了一个小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
awk -vmax=15 '{len=length($0); if(len>=max){printf("%s, %d at line # %d %s\n", FILENAME, len, NR, $0);}}' file*