Bir dosyadaki en uzun satırı nasıl yazdırırım?


34

Bir dosyadaki en uzun satırı yazdırmak için en basit yöntemi arıyorum. Bazı googling yaptım ve şaşırtıcı bir şekilde bir cevap bulamadı gibi görünüyordu. Bir dosyadaki en uzun satırın uzunluğunu sık sık yazdırırım, ancak gerçekte en uzun satırın nasıl yazdırılacağını bilmiyorum. Bir dosyadaki en uzun satırı basmak için herhangi bir çözüm sağlayabilir mi? Şimdiden teşekkürler.


1
Birden fazla "en uzun" satır olduğunda ne olur? Basit bir maksimum uzunluktan daha fazlasını istediğiniz için, eşit uzunluktaki tüm satır örneklerini görmek ister misiniz?
Peter.O

Yanıtlar:


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

UPD : yorumlardaki tüm önerileri özetliyor

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

3
Hem bir komutu ( cat) çağırmak hem de bir pipo kullanmak pahalı işlemlerdir, awk'nin dosyayı okumanın daha verimli olduğunu söylemekten değil. Bu sık sık yapılırsa performans sonuçları kesinlikle fark edilir ve hatta öyle olsa bile, tamamen kötüye kullanıyorsunuz cat.
Chris Down,

7
@ laebshade Kesinlikle bir nedeni var - bu yüzden hangi komutların dosya isimlerini aldığını ve hangilerinin boru hattında ilk önce hangi komutun uygulanacağına dikkat etmenize gerek olmadığını hatırlamanıza gerek yok. Sık sık çalıştırılan bir komut dosyası yazacaksanız, elbette böyle bir şey için endişelenin. Bir dosyadaki en uzun satırı bulmak için bir kereye mahsus bir şey yazıyorsanız, harcanan fazladan işlem ve harcanan süre tamamen anlamsızdır. İnsanların burada buna takıntılı olması aptalca, inanılmaz derecede küçük
Michael Mrozek

4
@Keith Thompson: catBurada işe yaramaz değil. Bir bilgisayar için işe yaramaz olabilir ama bir insan okuyucu için değer sağlayabilir. İlk değişken açık bir şekilde girişi gösterir. Akış daha doğaldır (soldan sağa). İkinci durumda, pencereyi kaydırmadan girişin ne olduğunu bilmiyorsunuzdur.
jfs

1
@ JFSebastian Solda istemeseniz bile, gerekmez cat. < file commandsadece iyi çalışıyor.
Chris Down

3
@ JFSebastian: Bir yönlendirmenin komutun başında yazılabileceği gerçeği biraz belirsizdir; Ben denedim her kabukta < filename commandeşdeğerdir filename < command. Ancak bunun farkında olduğunuzda, veri akışının yönünü açıkça gösteren uzun borular yazarken (ekstra bir komut çağırmadan) bundan yararlanabilirsiniz:< input-file command1 | command2 | command3 > output-file
Keith Thompson

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

+1 Bunun için çok ilginç çözümler vardı ama bu en basit olanıydı. (
Awk'nin

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

Bu, ilk önce komut değiştirme içindeki dosyayı okur ve en uzun satırın uzunluğunu çıkarır (daha önce, expandsekmeleri anlamsal olarak aşmak için sekmeleri boşluklara dönüştürür wc -L- satırdaki her sekme satır başına 1 yerine 8 ekler ). Bu uzunluk daha sonra sed"bu kadar karakter uzunluğunda bir satır bulun, yazdırın, sonra çıkın" anlamına gelen bir ifadede kullanılır . Yani bu aslında en uzun satır dosyanın en üstünde olduğu kadar optimal olabilir, heheh (müthiş ve yapıcı yorumlar için teşekkürler).

Bir başkası, bash olandan daha erken düşünmüştüm:

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

2
Bu yöntem çok pahalı ve yavaştır.
Chris Down,

2
@ Chris Aşağı: Ah evet. Ancak soru, en verimli olanın değil, sıralama yöntemiyle ilgiliydi. W olsa, küçük veya orta büyüklükteki dosyalar veya kritik olmayan işler için ince.
ata

3
UYARI : wc'nin seçeneği -L, --max-line-length, man sayfasına göre en uzun çizginin uzunluğunu yazdırır, ancak daha derine inerseniz ( yanlış / beklenmedik sonuçlar elde ettiğinizde olduğu gibi ), bu seçeneğin her 1 sekme için uzunluğu 8 artırdığını görürsünüz. bakın bu Unix ve Linux Q / A\x09
Peter.O

PS. Tüm muhtemelen bir "eşit en uzun" çizgileri, yazdırır Yanıtınız iyi ... şey zorlamak için wc sekmesi başına sadece 1 kömürü, bu eserleri saymak. sed -rn "/.{$(<file expand -t1 |wc -L)}/p" file
Peter,

1
read lineliteral char olarak ters eğik çizgi bulunan karakter, örneğin yorumlayacak \Aresloves için ABunu önlemek için ... Tabii etkili bir bir gerçek daha kısa bayt kullanımını bildiriyor, kaçan kullanım yorumunu,: read -r line. . . . Ayrıca yapmak için sed + wc sürümü ilk "en uzun hat" sonra çıkın, değişim piçin {p;q}..sed -rn "/.{$(<file expand -t1 |wc -L)}/{p;q}" file
Peter.O

4

İşte bir Perl çözümü:

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

Veya, yazdırmak istiyorsanız tüm en uzun satırları

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

Yapacak daha iyi bir şeyim olmadığından, 625M metin dosyasında bazı ölçütler koştum. Şaşırtıcı bir şekilde, Perl çözümüm diğerlerinden daha tutarlıydı. Kabul edildi, kabul edilen awkçözüm ile fark küçük, ama var. Açıkçası, birden çok satır yazdıran çözümler yavaştır, bu yüzden türüne göre sıraladım, en hızlıdan en yavaşa kadar.

En uzun satırlardan yalnızca birini yazdırın:

$ 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

En uzun satırları yazdır:

$ 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

İlk uzun çizgi Grep

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

Bu komut alışılmadık bir şekilde pratik yapmadan okumak zordur çünkü kabuk ve regexp sözdizimini karıştırır.
Açıklama için önce basitleştirilmiş sözde kod kullanacağım. İle başlayan satırlar ##kabukta çalışmaz.
Bu basitleştirilmiş kod, F dosya adını kullanır ve okunabilirlik için alıntı ve regexps bölümlerini dışarıda bırakır.

Nasıl çalışır

Komut iki bölümden oluşur: a grep- ve bir wcçağrı:

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

wcBir süreç genişleme kullanılır, $( ... )bu yüzden önce çalıştırılır grep. En uzun çizginin uzunluğunu hesaplar. Kabuk genişleme sözdizimi, düzenli ifade deseni sözdizimi ile karıştırıcı bir şekilde karıştırılır, bu yüzden işlem genişlemesini parçalayacağım:

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

Burada işlem genişletme, döndürdüğü değerle değiştirildi ve grepkullanılan komut satırı oluşturuldu . Artık normal ifadeyi daha kolay okuyabiliriz: Tam olarak satırın başlangıcından ( ^) sonuna ( $) kadar eşleşir . Aralarındaki ifade, 42 satır tekrarlanan yeni satır dışındaki herhangi bir karakterle eşleşir. Kombine, tam olarak 42 karakterden oluşan satırlar.


Şimdi, gerçek kabuk komutlarına geri dönelim: ( ) grepseçeneği , okunabilirlikten kaçmamayı sağlar. Option ( ) ilk satır bulunduktan sonra durmasını sağlar. İçinde komuta önlemek için, onun stdin'e dosyasına yazar uzunluğu ile birlikte dosya adını yazdırmasını.-E--extended-regexp{}-m 1--max-count=1<wcwc

En uzun çizgiler hangileri?

Örnekleri iki kez gerçekleşen dosya adıyla daha okunaklı hale getirmek fiçin, dosya adı için bir değişken kullanacağım ; $fÖrnekteki her biri dosya adıyla değiştirilebilir.

f="file.txt"

Göster ilk uzun çizgi - en uzun hat sürece ilk satırı:

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

Tüm en uzun satırları göster - en uzun satır kadar uzun olan tüm satırlar:

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

En uzun çizgiyi göster - en uzun çizgi kadar uzun olan son çizgi:

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

En uzun satırı göster - diğer tüm satırlardan daha uzun olan en uzun satırı göster

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

(Komut grep komutunu tekrar ettiği için, son komut diğerlerinden daha fazla verimsizdir. Açıkçası, çıktısının wcve yazılan satırların grepdeğişkenlere kaydedileceği şekilde ayrıştırılmalıdır. En
uzun satırların gerçekte tüm satırların olabileceğini unutmayın . Bir değişkeni kaydetmek için, sadece ilk iki satırın tutulması gerekir.)


Vay büyük cevap, ondan çok şey öğrendim. teşekkürler
şey

2

Aşağıdaki örnek, dmitry.malikov'un cevabı için bir yorum olacaktı ve olmalıydı , ama orada Görkemli Yorum Alanını Kullanmanın Yararsız Kullanımı nedeniyle, burada, en azından görüleceği yerde sunmayı seçtim. ..

Bu, alümemin tek geçişli awk yönteminin basit bir varyasyonudur .
Tüm "eşit en uzun" satırları yazdırır. (Not. delete arrayBir gawk uzantısıdır).

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

1

Saf bash'ta:

#!/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[@]}"

Olduğu gibi, kod geçersiz sonuçlar verebilir. Ayar _max_line[0]=${_line}, daha önce biriken daha kısa "en uzun satırların" geri kalan kısmını kaldırmaz ... unset _max_linetüm diziyi temizler ...
Peter.O

@fered Bunun için teşekkürler, oldukça hızlı bir şekilde yazılmıştır. Sabit.
Chris Down

0

Bunun için küçük bir kabuk betiği geliştirdim. Uzunluk, satır # ve satırın kendisini 80 karakter gibi belirli bir boyutu geçen uzunluğa göre görüntüler:

#!/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
Yapabileceğiniz birkaç gelişme var. Değişkenlerinizden alıntı yapın . Bu, boşluk veya diğer garip karakterler içeren dosya adlarını kırar. Kullanmak $*nadiren iyi bir fikirdir, istersiniz"$@" . /.*/Gözlerinde farklı awkolduğu sıra boş satırları maçları beri hiçbir şey yapmaz. Eğer \$0tek alıntı yaparsanız , kaçmayı önleyebilirsiniz 'EOF'. Neden boş bir BEGIN{}blok kullanılmalı? Sonunda, ihtiyacınız yok cat, sadeceawk . . . "$file" | . . .
terdon

1
Her şeyi doğrudan awk'de de yapabilirsiniz:awk -vmax=15 '{len=length($0); if(len>=max){printf("%s, %d at line # %d %s\n", FILENAME, len, NR, $0);}}' file*
terdon

-3

Kullanabilirsiniz wc:

wc -L fileName

3
Lütfen soruyu tekrar okuyun. İstenilen çıktı, en uzun satırın uzunluğu değil, en uzun satırın kendisidir. Ayrıca Peter.O'nunwc -L dezavantajı ile ilgili yorumuna bakınız .
Manatwork
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.