Як надрукувати найдовший рядок у файлі?


35

Я шукаю найпростіший метод для друку найдовшого рядка у файлі. Я трохи погукав і на диво не міг знайти відповіді. Я часто друкую довжину найдовшого рядка у файлі, але не знаю, як насправді надрукувати найдовший рядок. Чи може хтось надати рішення для друку найдовшого рядка у файлі? Заздалегідь спасибі.


1
А як бути, коли є кілька "найдовших" рядків ?. Оскільки ви хочете більше простої максимальної довжини, ви хочете бачити всі екземпляри рядків, які однаково довгі?
Пітер.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 Тут абсолютно є причина - тому вам не потрібно пам’ятати, які команди беруть назви файлів, а які - ні, або не важливо, яка команда буде виконана першою в конвеєрі. Якщо ви збираєтеся писати сценарій, який часто запускається, будь-ласка, турбуйтеся про щось подібне. Якщо ви пишете разову річ, щоб знайти найдовший рядок у файлі, зайвий процес та дробова витрата часу зовсім не мають значення. Дурне, що люди настільки одержимі цим тут, це неймовірно незначно
Michael Mrozek

4
@Keith Thompson: catтут марно. Це може бути марним для комп'ютера, але для людського читача це може забезпечити цінність. Перший варіант чітко показує вхідні дані. Течія більш природна (зліва направо). У другому випадку ви не знаєте, що таке вхід, якщо ви не прокрутите вікно.
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- кожна вкладка у рядку додасть 8 замість 1 до довжини рядка). Потім ця довжина використовується у sedвиразі, що означає "знайдіть рядок, який має цю кількість символів, надрукуйте його та вийдіть". Таким чином, це насправді може бути настільки ж оптимальним, оскільки найдовший рядок знаходиться біля верхньої частини файлу, хе-хе (спасибі, що боїться за приголомшливі та конструктивні коментарі)

Інше, я думав раніше, ніж sed (у басі):

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

2
Цей метод дуже дорогий і повільний.
Кріс Даун

2
@Chris Down: О так. Але питання полягало у сортувальному методі, не найефективнішому. Хоча добре працює для невеликих та середніх файлів або некритичних завдань.
ата

3
ПОПЕРЕДЖЕННЯ : опція wc -L, --max-line-lengthдрукує довжину найдовшого рядка відповідно до сторінки man, але якщо ви копаєте глибше (як у випадку, коли ви отримаєте неправильні / несподівані результати), ви виявите, що ця опція збільшує довжину на 8 для кожної таблиці 1 табуляції \x09 дивіться це питання для Unix & Linux Q / A
Peter.O

PS. Ваша відповідь надрукує всі "однаково довгі" рядки, що, мабуть, добре ... Змусити wc рахувати лише 1 char на вкладці, це працює. sed -rn "/.{$(<file expand -t1 |wc -L)}/p" file
Пітер.O

1
read lineбуде інтерпретувати зворотні косу риску символів як буквальний напівкокс, наприклад \Aresloves до 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рішенні невелика, але вона є. Очевидно, що рішення, які друкують кілька рядків, повільніші, тому я сортував за типом, найшвидший до найповільнішого.

Друк лише однієї з найдовших ліній:

$ 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 -Em1 "^.{$(wc -L <file.txt)}\$" file.txt 

Команду надзвичайно важко читати без практики, оскільки вона змішує синтаксис оболонки та регулярного виразів.
Для пояснення спочатку скористаюся спрощеним псевдокодом. Рядки, що починаються з## , не проходять в оболонці.
Цей спрощений код використовує ім'я файлу F і не залишає цитування та частин регулярних виразів для читабельності.

Як це працює

Команда має дві частини, а 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записує файл на його stdin, щоб запобігти 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були збережені до змінних.
Зауважте, що всі найдовші рядки насправді можуть бути усіма рядками . Для збереження змінної потрібно зберігати лише перші два рядки.)


Вау чудова відповідь, багато чого навчився з неї. дякую
щось щось

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

Чистий баш:

#!/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/lolong-line/lolong-line.sh


1
Ви можете зробити кілька покращень. Цитуйте свої змінні . Це порушиться з будь-якими іменами файлів, що містять пробіли чи інші дивні символи. Використання $*рідко хороша ідея, ви хочете"$@" . У /.*/вашому awkне робиться нічого, оскільки це відповідає і порожнім рядкам. Ви можете уникнути уникнення, \$0якщо цитувати це 'EOF'. Навіщо використовувати порожній BEGIN{}блок? Нарешті, вам не потрібно cat, простоawk . . . "$file" | . . .
terdon

1
Ви також можете зробити все це в awk -vmax=15 '{len=length($0); if(len>=max){printf("%s, %d at line # %d %s\n", FILENAME, len, NR, $0);}}' file*
курсі

-3

Ви можете використовувати wc:

wc -L fileName

3
Будь ласка, прочитайте питання ще раз. Необхідний вихід - це найдовший рядок, а не довжина найдовшого рядка. Також дивіться коментар Peter.O щодо wc -Lнедоліку.
манатура
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.