ファイルの最も長い行を印刷する方法は?


35

ファイル内の最も長い行を印刷する最も簡単な方法を探しています。私はいくつかのグーグル検索を行いましたが、驚くべきことに答えを見つけることができませんでした。ファイル内の最も長い行の長さを頻繁に印刷しますが、実際に最も長い行を印刷する方法がわかりません。誰もがファイルの最長行を印刷するソリューションを提供できますか?前もって感謝します。


1
複数の「最も長い」行がある場合はどうですか。単純な最大長以上のものが必要なため、最長の等しい行のすべてのインスタンスを表示しますか?
Peter.O

回答:


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絶対的な理由があります。どのコマンドがファイル名を使用し、どのコマンドが使用しないかを覚えたり、パイプラインで最初に実行されるコマンドを気にする必要がないためです。頻繁に実行されるスクリプトを作成する場合は、必ずこのようなことを心配してください。ファイル内で最も長い行を見つけるために1回限りのことを書いている場合、余分なプロセスとわずかな時間の消費はまったく関係ありません。ここで人々がそれに夢中になっているのはばかげている、それは信じられないほどマイナーです
マイケルMrozek

4
@キース・トンプソン:catここでは無駄ではありません。それはコンピューターには役に立たないかもしれませんが、人間の読者にとっては価値を提供することができます。最初のバリアントは、入力を明確に示しています。流れはより自然です(左から右へ)。2番目のケースでは、ウィンドウをスクロールしない限り、入力が何であるかわかりません。
jfs

1
@JFSebastian左側にある場合でも、必要ありませんcat< file commandうまく動作します。
クリスダウン

3
@JFSebastian:コマンドの先頭にリダイレクトを記述できるという事実は、やや不明瞭です。私が試したすべてのシェル< filename commandと同等filename < commandです。しかし、それをあなたがしている気づいたら、長いパイプを書くとき、あなたは明らかに(余分なコマンドを呼び出さず)データフローの方向を示していること、それを活用することができます:< input-file command1 | command2 | command3 > output-file
キース・トンプソン

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

+1これには多くの興味深い解決策がありましたが、これは最も簡単でした。(猫がなくてもawkにファイルを読み取らせることで簡単になりますが、なぜいですか?)
user1683793

5
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"

2
この方法は非常に高価で時間がかかります。
クリスダウン

2
@クリスダウン:ああそうです。しかし、問題は最も効率的な方法ではなく、最もソート方法についてでした。ただし、小規模から中規模のファイルや重要ではないタスクには最適です。
ATA

3
警告:wcのオプション-L, --max-line-lengthは、manページによると、最も長い行の長さを出力しますが、(間違った/予期しない結果が得られ場合のように)深く掘り下げると、このオプションは1タブ文字ごとに8ずつ長さを増やします このUnix&Linuxに関するQ / Aを参照してください\x09
Peter.O

PS。あなたの答えは、すべての「等しい最長」行を印刷します。これはおそらく良いことです... wcにタブごとに1文字だけをカウントさせるようにするには、これは機能します。sed -rn "/.{$(<file expand -t1 |wc -L)}/p" file
Peter.O

1
read lineは、バックスラッシュでエスケープされた文字をリテラル文字、たとえば\Aresloves to として解釈します。Aもちろん、実際のバイト使用量よりも短いことを効果的に報告します...このエスケープされた解釈を防ぐに、を使用しますread -r line。。。。また、作るためのsed + WCバージョンは最初の「最も長い行」の後に終了し、変更p{p;q}...sed -rn "/.{$(<file expand -t1 |wc -L)}/{p;q}" file
Peter.O

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ソリューションとの違いはわずかですが、そこにあります。明らかに、複数の行を印刷するソリューションは遅いので、タイプ別にソートしました。

最も長い行の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

3

最初の最長行をGrep

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)は、最初の行が見つかった後に停止します。コマンドは防ぐために、その標準入力にファイルを書き込み、長さと一緒にファイル名を印刷するから。<wcwc

どの最長ラインですか?

ファイル名を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行のみを保持する必要があります。


すごい答え、それから多くを学びました。ありがとう
何か

2

次の例は、dmitry.malikovの回答に対するコメントになるはずでした が、そこに表示されているコメントスペースの無用な使用のため、少なくともここでそれを表示することを選択しました。 ..

これは、dmitryの シングルパスawkメソッド単純なバリエーションです。
すべての「等しい最長」行を印刷します。(注:delete arraygawk拡張です)。

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

@feredそのおかげで、かなり早く書かれました。一定。
クリスダウン

0

このための小さなシェルスクリプトを開発しました。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{}ブロックを使用する理由 最後に、必要はありませんcat、ただawk . . . "$file" | . . .
terdon

1
また、単に直接のawkで全体のことを行うcouuld: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
マナトワーク
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.