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
... збігається з s
in \1
та ''
null string в \2
.
1\nstring2\nstring3
... поєднується з 1
в \1
і \nstring2\nstring3
в\2
\nstring2\nstring3
... збігається з \n
in \1
та ''
null string в \2
. Це було б проблематично, якби є шанс виникнення \n
ewline на чолі простору шаблону - але для запобігання цього використовуються команди /^\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
.