回答:
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
ここでは無駄ではありません。それはコンピューターには役に立たないかもしれませんが、人間の読者にとっては価値を提供することができます。最初のバリアントは、入力を明確に示しています。流れはより自然です(左から右へ)。2番目のケースでは、ウィンドウをスクロールしない限り、入力が何であるかわかりません。
cat
。< file command
うまく動作します。
< filename command
と同等filename < command
です。しかし、それをあなたがしている気づいたら、長いパイプを書くとき、あなたは明らかに(余分なコマンドを呼び出さず)データフローの方向を示していること、それを活用することができます:< input-file command1 | command2 | command3 > output-file
cat filename | awk '{ print length }' | sort -n | tail -1
sed -rn "/.{$(<file expand -t1 |wc -L)}/{p;q}" file
これは最初にコマンド置換内のファイルを読み取り、最も長い行の長さを出力しexpand
ます(以前は、タブをスペースに変換して、セマンティクスを克服しましたwc -L
-行の各タブは、行の長さに1ではなく8を追加します)。この長さは、sed
「この文字数の行を見つけて印刷し、終了する」という意味で使用されます。したがって、これは実際には、最長の行がファイルの先頭近くにあるのと同じくらい最適な場合があります。
別の、私はsed(bash)よりも先に考えていました:
#!/bin/bash
while read -r line; do
(( ${#line} > max )) && max=${#line} && longest="$line"
done
echo "$longest"
-L, --max-line-length
は、manページによると、最も長い行の長さを出力しますが、(間違った/予期しない結果が得られた場合のように)深く掘り下げると、このオプションは1タブ文字ごとに8ずつ長さを増やします このUnix&Linuxに関するQ / Aを参照してください\x09
sed -rn "/.{$(<file expand -t1 |wc -L)}/p" file
read line
は、バックスラッシュでエスケープされた文字をリテラル文字、たとえば\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
ソリューションとの違いはわずかですが、そこにあります。明らかに、複数の行を印刷するソリューションは遅いので、タイプ別にソートしました。
最も長い行の1つだけを印刷します。
$ 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
このコマンドは、シェル構文と正規表現構文が混在しているため、実践せずに読むのは非常に困難です。
説明のために、単純化された擬似コードを最初に使用します。で始まる行##
はシェルで実行されません。
この簡略化されたコードはファイル名Fを使用し、読みやすくするために引用符と正規表現の一部を省略しています。
コマンドには2つの部分、grep
-とwc
呼び出しがあります。
## grep "^.{$( wc -L F )}$" F
wc
プロセスの拡張に使用されている、$( ... )
ので、前に実行されますgrep
。最も長い線の長さを計算します。シェル拡張構文は、混乱を招くような方法で正規表現パターン構文と混合されているため、プロセス拡張を分解します。
## wc -L F
42
## grep "^.{42}$" F
ここでは、プロセスの展開が返される値に置き換えられ、grep
使用されるコマンドラインが作成されます。正規表現をより簡単に読み取ることができるようになりました。行の開始(^
)から終了($
)まで正確に一致します。それらの間の式は、改行を除く任意の文字に一致し、42回繰り返されます。つまり、正確に42文字で構成される行です。
さて、実際のシェルコマンドに戻ります:grep
オプション-E
(--extended-regexp
)は{}
、読みやすくするためにをエスケープしないようにします。オプション-m 1
(--max-count=1
)は、最初の行が見つかった後に停止します。コマンドは防ぐために、その標準入力にファイルを書き込み、長さと一緒にファイル名を印刷するから。<
wc
wc
ファイル名を2回使用して例を読みやすくする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
変数に保存されるようにする必要があります。
。変数に保存するには、最初の2行のみを保持する必要があります。
純粋な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
...配列全体をクリアします
このための小さなシェルスクリプトを開発しました。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*
以下を使用できますwc
。
wc -L fileName
wc -L
。