Як знайти рядок з найменшими символами


22

Я пишу сценарій оболонки, використовуючи будь-які загальні команди UNIX. Я повинен отримати рядок, що містить найменше символів (включено пробіл). Тут може бути до 20 рядків.

Я знаю, що можу використати head -$L | tail -1 | wc -mдля пошуку числа символів рядка L. Проблема полягає в тому, що єдиним методом, про який я можу придумати, використовуючи це, було б вручну написати безлад, якщо заяви, порівнюючи значення.

Приклад даних:

seven/7
4for
8 eight?
five!

Повернеться, 4forоскільки цей рядок мав найменше символів.

У моєму випадку, якщо кілька рядків мають найкоротшу довжину, слід повернути один. Не має значення, який з них обраний, якщо він має мінімальну довжину. Але я не бачу шкоди в тому, щоб показувати обидва способи іншим користувачам у інших ситуаціях.


5
Що робити, якщо є кілька рядків довжиною 4? Чи слід надрукувати їх теж?
хаос

У моєму випадку, якщо кілька рядків мають найкоротшу довжину, слід повернути один. Не має значення, який з них обраний, якщо він має мінімальну довжину. Але я не бачу шкоди в тому, щоб показувати обидва способи іншим користувачам в інших ситуаціях.
Меттью Д. Скоулфілд

Відповіді:


13

Перл-шлях. Зауважте, що якщо існує багато рядків однакової, найкоротшої довжини, цей підхід буде надрукувати лише один з них:

perl -lne '$m//=$_; $m=$_ if length()<length($m); END{print $m if $.}' file 

Пояснення

  • perl -lne: -nозначає "читати вхідний файл рядок за рядком", -lвикликає видалення останніх рядків з кожного вхідного рядка та додавання нового рядка до кожного printвиклику; і -eце сценарій, який буде застосовано до кожного рядка.
  • $m//=$_: встановити $mпоточний рядок ( $_), якщо $mне визначено. //=Оператор доступний , так як Perl 5.10.0.
  • $m=$_ if length()<length($m) : якщо довжина поточного значення $m більше, ніж довжина поточного рядка, збережіть поточний рядок ( $_) як $m.
  • END{print $m if $.}: після обробки всіх рядків надрукуйте поточне значення $m, найкоротший рядок. У if $.гарантує , що це відбувається тільки тоді , коли номер рядка ($. ) визначається, уникаючи друку порожнього рядка для введення порожній.

Крім того, оскільки ваш файл досить малий, щоб вміститися в пам'яті, ви можете:

perl -e '@K=sort{length($a) <=> length($b)}<>; print "$K[0]"' file 

Пояснення

  • @K=sort{length($a) <=> length($b)}<>: <>ось масив, елементами якого є рядки файлу. ThesortСортує їх по довжині і впорядковані рядки зберігаються в вигляді масиву @K.
  • print "$K[0]": вивести перший елемент масиву @K: найкоротший рядок.

Якщо ви хочете надрукувати всі найкоротші рядки, можете скористатися

perl -e '@K=sort{length($a) <=> length($b)}<>; 
         print grep {length($_)==length($K[0])}@K; ' file 

1
Додати, -Cщоб виміряти довжину в кількості символів замість кількості байтів. У локалі UTF-8 $$має менше байтів, ніж (2 проти 3), але більше символів (2 проти 1).
Стефан Шазелас

17

З sqlite3:

sqlite3 <<EOT
CREATE TABLE file(line);
.import "data.txt" file
SELECT line FROM file ORDER BY length(line) LIMIT 1;
EOT

Цей мій улюблений тут, ніколи не думав про SQL ...
хаос

2
Це код гольфу статус розумний
shadowtalker

2
Чи прочитає це весь файл в пам'ять та / або створить другу копію на диску? Якщо це так, це розумно, але неефективно.
Джон Кугельман підтримує Моніку

1
@JohnKugelman Це, ймовірно, занурить цілі 4 рядки у базу даних лише для тимчасової пам'яті (саме це straceвказує). Якщо вам потрібно працювати з дуже великими файлами (а ваша система не замінюється), ви можете примусити її, просто додавши назву файлу на зразок, sqlite3 $(mktemp)і всі дані будуть записані на диск.
FloHimself

Я отримую такі помилки: "" "xaa: 8146: немальований" символ "" "і" "xaa: 8825: очікується 1 стовпчик, але знайдено 2 - додаткові ігноруються" "". Файл складається з json документів 1 у кожному рядку .
Ахмедов

17

Ось варіант awkрішення для друку першого знайденого мінімального рядка:

awk '
  NR==1 || length<len {len=length; line=$0}
  END {print line}
'

який можна просто подовжити на одну умову для друку всіх мінімальних рядків:

awk '
  length==len {line=line ORS $0}
  NR==1 || length<len {len=length; line=$0}
  END {print line}'
'

12

Python виходить досить лаконічним, і код робить те, що говорить про олово:

python -c "import sys; print min(sys.stdin, key=len),"

Остаточна кома незрозуміла, я визнаю. Це заважає оператору друку додавати додатковий рядок. Крім того, ви можете написати це в Python 3, підтримуючи 0 рядків, таких як:

python3 -c "import sys; print(min(sys.stdin, key=len, default='').strip('\n'))"


що говорить олово?
mikeserv

@mikeserve: в ній сказано, "друкується мінімум sys.stdin, використовуючи len як ключ" ;-)
Стів Джессоп

1
ах. нічого не стосується двійкового розміру, повзання залежності або часу виконання?
mikeserv

2
@mikeserv: ні, дрібний шрифт не на бляшанці. Це на дорадчій листівці у замкненій шафі для подачі, у погребі, за дверима з написом "остерігайся леопарда".
Стів Джессоп

Gotcha - так на показ.
mikeserv

10

Я завжди люблю рішення із сценарієм чистої оболонки (без виконання!).

#!/bin/bash
min=
is_empty_input="yes"

while IFS= read -r a; do
    if [ -z "$min" -a "$is_empty_input" = "yes" ] || [ "${#a}" -lt "${#min}" ]; then
        min="$a"
    fi
    is_empty_input="no"
done

if [ -n "$a" ]; then
    if [ "$is_empty_input" = "yes" ]; then
        min="$a"
        is_empty_input="no"
    else
        [ "${#a}" -lt "${#min}" ] && min="$a"
    fi
fi

[ "$is_empty_input" = "no" ] && printf '%s\n' "$min"

Примітка :

У вході є проблема з байтами NUL. Отже, printf "ab\0\0\ncd\n" | bash this_scriptдрукує abзамість cd.


Це справді найчистіше. Хоча незграбність тестів у цьому bashпереконає мене sortзамість цього проміжного результату .
orion

2
Ви спробували встановити свій noeeec! рішення проти інших, які роблять? Ось порівняння відмінностей у виконанні між exec! і жодного виконавця! рішення подібної проблеми. виконувати окремий процес дуже рідко вигідно, коли він павуків - у таких формах, як var=$(get data)тому, що він обмежує потік даних до одного контексту, - але коли ви переміщуєте дані по конвеєру - в потоці - кожен застосований виконавець, як правило, корисний - тому що він дозволяє спеціалізуватися застосування модульних програм лише там, де це необхідно.
mikeserv

1
@DigitalTrauma - розширений суміжний рядок цифр не є більш-менш звільненим від умов, які роблять необхідним цитування оболонок, ніж будь-який інший розширений рядок. $IFSне є дискримінаційним - навіть якщо у $IFSзначенні за замовчуванням їх немає , хоча багато оболонок приймуть попередньо задану конфігурацію середовища $IFS- і так це не є особливо надійним за замовчуванням.
mikeserv


1
Дякую всім за коментарі та відгуки (деякі представники повинні перейти на @cuonglm для виправлення моєї відповіді). Як правило, я не рекомендую іншим щодня практикувати сценарій чистої оболонки, але цей навик може бути дуже корисним в екстремальних умовах, коли немає нічого, крім статичного зв'язку /bin/sh. Зі мною траплялося кілька разів із хостами SunOS4 із /usrвтраченими або деякими .soпошкодженими, і зараз у сучасній епохи Linux я все ще час від часу стикаюся з подібними ситуаціями із вбудованими системами або з initrd збою систем завантаження. BusyBox - одна з чудових речей, які ми нещодавно придбали.
yaegashi

9

Ось чисте zshрішення (воно друкує всі рядки з мінімальною довжиною, від file):

IFS=$'\n'; print -l ${(M)$(<file):#${~${(o@)$(<file)//?/?}[1]}}

Приклад введення:

seven/7
4for
8 eight?
five!
four

Вихід:

4for
four

Я думаю, що це потребує короткого пояснення :-)


Спочатку встановлюємо внутрішній роздільник поля на новий рядок:

IFS=$'\n';

Поки що хороша, тепер важка частина. printвикористовує -lпрапор для друку результату, розділеного новими рядками, а не пробілами.

Тепер ми починаємо з внутрішньої сторони:

$(<file)

Файл читається рядок за рядком і трактується як масив. Потім:

${(o@)...//?/?}

У oпрапорі написано, що результат слід впорядковувати у порядку зростання, а @засоби вважати результат також масивом. Частина поза ( //?/?) - це заміна, яка замінює всі символи знаком "a" ?. Зараз:

${~...[1]}

Ми беремо перший елемент масиву [1], який є найкоротшим, у вашому випадку його зараз ????.

${(M)$(<file):#...}

Узгодження виконується для кожного елемента масиву окремо, а незрівняні елементи масиву видаляються ( M). Кожен елемент, який відповідає ????(4 символи), залишається в масиві. Тож решта елементів - це ті, які мають 4 символи (найкоротші).

Редагувати: Якщо вам потрібна лише одна з найкоротших рядків, ця змінена версія друкує першу:

IFS=$'\n'; print -l ${${(M)$(<file):#${~${(o@)$(<file)//?/?}[1]}}[1]}

8
tr -c \\n 1 <testfile |   #first transform every [^\n] char to a 1
grep -nF ''           |   #next get line numbers
paste -d: - testfile  |   #then paste it together with itself
sort  -t: -nk2,2          #then sort on second field

... а переможець ... рядок 2, здавалося б.

2:1111:4for
4:11111:five!
1:1111111:seven/7
3:11111111:8 eight?

Але проблема в тому, що кожен рядок повинен перевищувати вдвічі більше, щоб він працював - так що LINE_MAX фактично вдвічі зменшується. Причина в тому, що він використовує - що, база 1? - представляти довжину рядка. Схожий - і, можливо, більш охайний - підхід може бути стисненням цієї інформації в потоці. Перша ідея, що виникає у мене, полягає в тому, що я повинен unexpandїй:

tr -c \\n \  <testfile    |   #transform all [^\n] to <space>
unexpand -t10             |   #squeeze every series of 10 to one tab
grep -nF ''               |   #and get the line numbers
sed    's/:/!d;=;:/;h;:big    #sed compares sequential lines
$P;$!N; /\(:[^ ]*\)\( *\)\n.*\1.*\2/!D     #newest line is shorter or...
        g;/:./!q;b big'   |   #not; quit input entirely for blank line
sed -f - -e q testfile        #print only first occurrence of shortest line

Це друкує ...

2
4for

Ще один, просто sed:

sed -n '/^\n/D;s/\(.\)\(\n.*\)*/\1/g
$p;h;   s// /g;G;x;n;//!g;H;s// /g
G;      s/^\( *\)\(\n \1 *\)\{0,1\}\n//
D'      <infile >outfile

Синтаксис відповідає стандартам - але це не є гарантією того, що будь-який старий sedбуде \(reference-group\)\{counts\}правильно поводитися - багато хто цього не робить.

В основному він застосовує один і той же регулярний вивід для введення неодноразово - що може бути дуже корисно, коли настав час їх компілювати. Цей візерунок:

\(.\)\(\n.*\)*

Який по-різному відповідає різним рядкам. Наприклад:

string1\nstring2\nstring3

... збігається з sin \1та ''null string в \2.

1\nstring2\nstring3

... поєднується з 1в \1і \nstring2\nstring3в\2

\nstring2\nstring3

... збігається з \nin \1та ''null string в \2. Це було б проблематично, якби є шанс виникнення \newline на чолі простору шаблону - але для запобігання цього використовуються команди /^\n/Dта //!gкоманди. Я використовував, [^\n]але інші потреби цього маленького сценарію викликали стурбованість, і я не був задоволений багатьма способами, які часто трактуються неправильно. Плюс, .швидше.

\nstring2
string1

... збігаємося \nі sзнову в \1і обидва отримуємо ''нульовий рядок \2. Порожні рядки взагалі не збігаються.

Коли модель застосовується gлокально, два зміщення - як найменш лівий зсув, так і менший зсув правої сторони \n- врівноважуються, щоб здійснити пропуск. Кілька прикладів:

s/\(.\)\(\n.*\)*/\1:\2/g
s/\(.\)\(\n.*\)*/\2\1:/g
s/\(.\)\(\n.*\)*/\1: /g
s/\(.\)\(\n.*\)*/ :\2/g

... якщо все застосовується (не підряд) до наступного рядка ...

string1\nstring2

... перетворить його на ...

s:t:r:i:n:g:1:\nstring2
s:t:r:i:n:g:\nstring21:
s:t:r:i:n:g:1: 
 : : : : : : :\nstring2

В основному я використовую регулярний параметр, щоб завжди обробляти тільки перший рядок у будь-якому просторі шаблону, до якого я його застосую. Це дозволяє мені жонглювати двома різними версіями як збереженої лінії найкоротшого збігу, так і останньої лінії, не вдаючись до тестових циклів - кожна застосована заміна обробляє одразу весь простір шаблону.

Різні версії необхідні для буквального порівняння рядків / рядків - тому повинна бути версія кожного рядка, де всі символи гарантовано рівні. Але, звичайно, якщо один чи інший повинен закінчитися фактично найшвидшим найкоротшим рядком введення, тоді, надрукований для виведення рядка, мабуть, має бути оригінальною версією рядка - не тією, яку я санітував / гомогенізував заради порівняння. І тому мені потрібні дві версії кожної.

Прикро, що ще одна необхідність - це багато перемикання буфера для роботи з тим же самим, - але принаймні, жоден буфер ніколи не перевищує більше чотирьох рядків, необхідних для того, щоб залишатися поточними - і тому, можливо, це не страшно.

У будь-якому випадку, для кожного циклу перше, що трапляється, - це перетворення на запам'ятовується рядку - адже єдиною збереженою копією є буквальний оригінал - у ...

^               \nremembered line$

... а потім nрядок введення ext замінює будь-який старий буфер. Якщо він не містить хоча б одного символу, він фактично ігнорується. Набагато простіше було б просто qвикористати перший пустий рядок, але, мої тестові дані мали багато таких, і я хотів обробити кілька абзаців.

Отже, якщо він містить символ, його буквальна версія додається до запам’ятованого рядка, а його відстань порівняна версія розміщується на голові простору шаблону, як це:

^   \n               \nremembered line\nnew$

Останнє заміщення застосовується до цього простору шаблону:

s/^\( *\)\(\n \1 *\)\{0,1\}\n//

Отже, якщо новий рядок може вміститись у простір, необхідний для збереження запам'ятовується рядка, принаймні одним знаком для запасного, тоді перші два рядки замінюються геть, а лише лише перший.

Незалежно від результату, перший рядок у просторі шаблону завжди Dвибирається в кінці циклу перед початком початку. Це означає, що якщо новий рядок коротший за останній рядок ...

new

... повертається до першої заміни в циклі, яка завжди зніме лише з першого символу нового рядка - і так вона залишиться цілою. Але якщо це не то рядок ...

remembered line\nnew

... замість цього розпочнеться наступний цикл, і перша заміна зніме з нього рядок ...

\nnew

...кожного разу.

На останньому рядку запам'ятовується рядок, який друкується для стандартного виходу, і тому для наведених прикладних даних він друкує:

4for

Але, серйозно, використовуйте tr.



Вам навіть потрібно вставити номери рядків? Моє читання ОП полягає в тому, що потрібна лише найкоротша лінія, а не обов'язково номер рядка цього рядка. Думаю, не буде шкоди в тому, щоб показати його на повноту.
Цифрова травма

@DigitalTrauma - ні, мабуть, ні. Але навряд чи дуже корисно без них - і вони приходять так дешево. Під час роботи потоку я завжди вважаю за краще включати засоби, що відтворюють оригінальний вхід однаково у висновку - номери рядків роблять це можливим тут. Наприклад, щоб включити результати першого навколо трубопроводу: REINPUT | sort -t: -nk1,1 | cut -d: -f3-. А другий - це проста справа, включаючи ще один sed --expressionсценарій у хвіст.
mikeserv

@DigitalTrauma - ой, і в першому прикладі номер рядків дійсно впливає на sortповедінку «s в якості сполучного вимикача , коли ж довжина лінія виникає на вході - так найраніші зустрічаються лінії завжди спливає на поверхню в цьому випадку.
mikeserv

7

Спробуйте:

awk '{ print length, $0 }' testfile | sort -n | cut -d" " -f2- | head -1

Ідея полягає у тому, щоб awkспочатку надрукувати довжину кожного рядка. Це відобразиться як:

echo "This is a line of text" | awk '{print length, $0}'
22 This is a line of text

Потім використовуйте кількість символів, щоб сортувати рядки sort, cutпозбутися від лічильника та headзберегти перший рядок (той, що має найменше символів). Звичайно, ви можете використовувати tailдля отримання рядка з найбільшою кількістю символів у цьому випадку.

(Це було прийнято з цієї відповіді )


+1 для логіки, але це не спрацює у всіх випадках. Якщо два рядки мають однакову кількість символів і яка мінімальна. Це дасть вам лише перший рядок, з яким ви стикаєтесьhead -1
Сахіхі

Щоб отримати найдовший рядок, трохи ефективніше повернути сортування, ніж використовувати tail(як headможна вийти, як тільки виконано його роботу, не читаючи решти його вводу).
Toby Speight

@Thushi Використовуючи трохи регексу, після друку номерів рядків все, крім рядків з тим же номером, що і рядок 1, можна було видалити, виводячи таким чином усі найкоротші рядки.
Меттью Д. Скоулфілд

5

З POSIX awk:

awk 'FNR==1{l=$0;next};length<length(l){l=$0};END{print l}' file

Він не працюватиме, якщо більш ніж один рядок містить однакову кількість символів, і це також мінімально.
Сухі

@Thushi: Він повідомить про перший мінімальний рядок.
cuonglm

Так. Але це неправильно вихід? Навіть інші рядки мають мінімальну кількість символів.
Сухі

1
@Thushi: Це не згадується в вимозі ОП, очікуючи оновлення від ОП.
cuonglm

3
Я не думаю, Lщо найкращим листом було min
обрано

3

Позичення деяких ідей @ mikeserv:

< testfile sed 'h;s/./:/g;s/.*/expr length "&"/e;G;s/\n/\t/' | \
sort -n | \
sed -n '1s/^[0-9]+*\t//p'

Перший sedробить наступне:

  • h зберігає початковий рядок у буфер утримування
  • Замініть кожен символ у рядку на :- це для усунення будь-якої небезпеки введення коду
  • Замініть весь рядок на expr length "whole line"- це вираз оболонки, який може бути оцінений
  • Команда es - це розширення sed GNU для оцінки простору шаблону і повернення результату в простір шаблону.
  • G додає новий рядок та вміст простору утримування (початковий рядок) до простору шаблону
  • фінал sзамінює новий рядок на вкладку

Кількість символів тепер є числом на початку кожного рядка, тому sort -nсортує за довжиною рядка.

Потім фінал sedвидаляє всі, крім першого (найкоротшого) рядка та довжини рядка, та друкує результат.


1
@mikeserv Так, я думаю expr, тут краще. Так, eбуде породжена оболонка для кожного рядка. Я відредагував вираз sed таким чином, що він замінює кожну таблицю в рядку на a :до eval, що, на мою думку, повинно усунути будь-яку можливість введення коду.
Digital Trauma

Зазвичай я б xargs exprвирішив особисто - але, крім уникнення проміжних оболонок, це, мабуть, більше стилістична річ. Мені це подобається, все одно.
mikeserv

3

Мені прийшло в голову, що вся справа можлива в одному sedвиразі. Це не дуже:

$ sed '1h;s/.*/&\n&/;G;:l;s/\n[^\n]\([^\n]*\)\n[^\n]/\n\1\n/;tl;/\n\n/{s/\n.*//;x};${x;p};d' testfile
4for
$ 

Порушення цього:

1h            # save line 1 in the hold buffer (shortest line so far)
s/.*/&\n&/    # duplicate the line with a newline in between
G             # append newline+hold buffer to current line
:l            # loop start
s/\n[^\n]\([^\n]*\)\n[^\n]/\n\1\n/
              # attempt to remove 1 char both from current line and shortest line
tl            # jump back to l if the above substitution succeeded
/\n\n/{       # matches if current line is shorter
  s/\n.*//    # remove all but original line
  x           # save new shortest line in hold buffer
}
${            # at last line
  x           # get shortest line from hold buffer
  p           # print it
}
d             # don't print any other lines

BSD sed в OS X трохи більш вибагливий з новими рядками. Ця версія працює як для BSD, так і для GNU версій sed:

$ sed -e '1h;G;s/\([^\n]*\)\(\n\)\(.*\)/\1\2\1\2\3/;:l' -e 's/\(\n\)[^\n]\([^\n]*\n\)[^\n]/\1\2/;tl' -e '/\n\n/{s/\n.*//;x;};${x;p;};d' testfile
4for
$

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


@mikeserv From man sedв ОС X: "Послідовність виходу \ n відповідає новому символу, вбудованому в простір шаблону" . Тому я думаю, що GNU sed дозволяє \nв регулярному вираженні та в заміні, тоді як BSD дозволяє лише \nв регулярному вираженні, а не в заміні.
Цифрова травма

Запозичення \nз простору шаблонів є хорошою ідеєю і працює в другому s///виразі, але s/.*/&\n&/вираз вставляє \nв простір шаблону, де раніше його не було. Також BSD sed вимагає буквальних нових рядків після визначення міток та гілок.
Цифрова травма

1
Ці нові рядки є роздільниками параметрів - вам потрібні, щоб вони розмежовували будь-яку команду, яка може прийняти довільний параметр - принаймні, саме так пише специфікація. Специфікація також говорить, що sedскрипт має бути текстовим файлом, за винятком того, що він не повинен закінчуватися в новому рядку . Таким чином, зазвичай ви можете їх розмежовувати як окремі аргументи - sed -e :\ label -e :\ label2і так далі. Оскільки ви все 1hодно робите , ви можете просто переключитися на певну логіку, засновану на x;Hотриманні нової лінії - і ви можете обрізати провідну нову лінію з простору шаблону в кінці циклу, не тягнучи в нову лінію w / D.
mikeserv

@mikeserv Ніцца. Так, я вставив потрібний мені новий рядок, зробивши Gперший і змінивши s///вираз. Розбиття його за допомогою -eдозволяє всім рухатись по одній (довгій) лінії без буквальних нових рядків.
Digital Trauma

\nЕвакуації білд для sed«s LHS, також, і я думаю , що це твердження специфікації дослівно, за винятком того, що вирази дужки POSIX також білд таким чином , щоб всі символи втрачають своє особливе значення - (явно включаючи \\) - у межах однієї, крім дужок, тире як роздільник діапазону і крапка, дорівнює, каре, двокрапка для порівняння, еквівалентності, заперечення та класів.
mikeserv

2

Ще одне рішення Perl: зберігайте рядки у хеш-масивах, хеш-ключ - довжина рядка. Потім роздрукуйте рядки мінімальною клавішею.

perl -MList::Util=min -ne '
    push @{$lines{ length() }}, $_;
} END {
    print @{$lines{ min keys %lines }};
' sample 
4for

Ви можете використовувати push @{$lines{+length}};і print @{$lines{+min keys %lines}};для меншого набору :)
cuonglm

Якби я займався гольфом, я б також не використав змінну назву "лінії":perl -MList::Util=min -nE'push @{$l{+length}},$_}END{say@{$l{min keys%l}}' sample
glenn jackman

+1 для версії, що не використовується для гольфу (яка працює!), Хоч лише для варіанту print all . - perlстає дещо поважним для тих із нас, хто не відповідає партесній думці perl. До речі. гольф sayдрукує хибну порожню лінію в кінці виходу.
Пітер.О

2

Щоб отримати лише перший найкоротший рядок:

f=file; sed -n "/^$(sed 's/./1/g' $f | sort -ns | sed 's/././g;q')$/{p;q}" $f

Щоб отримати всі найкоротші вказівки, просто змініть {p;q}наp


Інший метод (дещо незвичний) - це sortзробити власне сортування за довжиною . Він відносно повільний навіть при коротких лініях і стає різко повільнішим у міру збільшення довжини лінії.
Однак мені здається, що ідея сортування за клавішами, що перекриваються, досить цікава. Я розміщую його на випадок, якщо інші можуть також вважати його цікавим / інформативним.

Як це працює:
Сортуйте за варіантами довжини одного ключа - key 1який охоплює весь рядок
Кожен наступний варіант ключа збільшує довжину ключа на один символ, аж до довжини найдовшого рядка файлу (визначається за wc -L)

Щоб отримати лише перший (відсортований) найкоротший рядок:

f=file; sort -t'\0' $(seq -f "-k1.%0.0f" $(<"$f" wc -L) -1 1) "$f" | head -n1

що те саме, що:

f=file.in; 
l=$(<"$f" wc -L)
k=$(seq -f "-k1.%0.0f" $l -1 1) 
sort -st'\0' $k "$f" | head -n1

2

Якщо пусті рядки не вважаються найкоротшим і що порожні рядки можуть існувати, буде працювати наступний чистий AWK:

awk '
    {
        len   = length;
        a[$0] = len
    }
    !len { next }
    !min { min = len }
    len < min { min = len }
    END {
        for (i in a)
            if (min == a[i])
                print i
    }
' infile.txt

2

Що з використанням сорту?

awk '{ print length($0) "\t" $0 }' input.txt | sort -n | head -n 1 | cut -f2-

1

З GNU awk

gawk '
    {
         a[length]=$0
    };
    END
    {
        PROCINFO["sorted_in"]="@ind_num_asc";
        for (i in a)
        {
            print a[i]; 
            exit
        }
    }
    ' file
  • Прочитайте кожен рядок у масиві, індексованому довжиною рядка.

  • Встановіть PROCINFO["sorted_in"]для @ind_num_ascпримусового сканування масиву, який буде впорядкований за індексом масиву, відсортованим за чисельністю

  • Налаштування PROCINFOспособом, що знаходиться вище, змушує першу в проходженні масиву підібрати лінію з найменшою довжиною. Тому надрукуйте перший елемент із масиву та вийдіть

Це має той недолік , будучи в nlognтой час як деякі з інших підходів nпід час


1

Метод інструментів оболонки середнього рівня, без sedабо awk:

f=inputfile
head -n $(xargs -d '\n' -L 1 -I % sh -c 'exec echo "%" | wc -c' < $f | 
          cat -n | sort -n -k 2 | head -1 | cut -f 1)  $f | tail -1

Було б непогано не $fзмінювати змінну; У мене є поняття, яке можливо можливо teeякось використати ...
agc
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.