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
... a zwycięzcą jest ... linia 2, wydaje się.
2:1111:4for
4:11111:five!
1:1111111:seven/7
3:11111111:8 eight?
Problem polega jednak na tym, że każda linia musi mieć ponad dwukrotną długość, aby mogła działać - więc LINE_MAX jest skutecznie zmniejszony o połowę. Powodem jest to, że używa - co, podstawa 1? - do reprezentowania długości linii. Podobnym - i być może bardziej uporządkowanym - podejściem może być kompresowanie tych informacji w strumieniu. Pierwszym pomysłem, który przychodzi mi do głowy, jest to, że powinienem to unexpandzrobić:
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
To drukuje ...
2
4for
Kolejny, po prostu 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
Składnia jest zgodna ze standardami - ale to nie gwarantuje, że każdy stary sedporadzi sobie \(reference-group\)\{counts\}poprawnie - wielu tego nie robi.
Zasadniczo stosuje to samo wyrażenie regularne do danych wejściowych wielokrotnie - co może być bardzo korzystne, gdy nadszedł czas na ich kompilację. Ten wzór to:
\(.\)\(\n.*\)*
Który pasuje do różnych ciągów znaków na różne sposoby. Na przykład:
string1\nstring2\nstring3
... jest dopasowywane z sin \1i ''łańcuchem zerowym w \2.
1\nstring2\nstring3
... jest dopasowane 1w \1i \nstring2\nstring3w\2
\nstring2\nstring3
... jest dopasowywane z \nin \1i ''łańcuchem zerowym w \2. Byłoby to problematyczne, gdyby istniała jakakolwiek szansa, że \newline pojawi się na początku przestrzeni wzorów - ale polecenia /^\n/Di //!gsłużą do tego. Korzystałem z niego, [^\n]ale inne potrzeby związane z tym małym skryptem sprawiły, że przenośność była problemem i nie byłem zadowolony z wielu sposobów, w jaki często jest on źle interpretowany. Plus .jest szybszy.
\nstring2
string1
... dopasowuj \ni sponownie do \1i oba uzyskują ''ciąg zerowy \2. Puste linie w ogóle się nie zgadzają.
Kiedy wzór jest nakładany globalnie, dwa odchylenia - zarówno odchylenie standardowe najbardziej lewe, jak i mniejsze \nodchylenie ewline po prawej stronie - są równoważone w celu pominięcia. Kilka przykładów:
s/\(.\)\(\n.*\)*/\1:\2/g
s/\(.\)\(\n.*\)*/\2\1:/g
s/\(.\)\(\n.*\)*/\1: /g
s/\(.\)\(\n.*\)*/ :\2/g
... jeśli wszystkie zastosowano (nie po kolei) do następującego ciągu ...
string1\nstring2
... przekształci go w ...
s:t:r:i:n:g:1:\nstring2
s:t:r:i:n:g:\nstring21:
s:t:r:i:n:g:1:
: : : : : : :\nstring2
Zasadniczo używam wyrażenia regularnego, aby zawsze obsługiwać tylko pierwszą linię w dowolnej przestrzeni wzorców, do której ją stosuję. To pozwala mi żonglować dwiema różnymi wersjami zarówno zachowanej linii o najkrótszym jak dotąd dopasowaniu, jak i najnowszej linii bez uciekania się do pętli testowych - każda zastosowana zamiana obsługuje całą przestrzeń wzorców naraz.
Różne wersje są niezbędne do dosłownego porównywania ciągów / ciągów - dlatego musi istnieć wersja każdego wiersza, w której wszystkie znaki są równe. Ale oczywiście, jeśli jedno lub drugie powinno faktycznie zakończyć się najwcześniejszą pojawiającą się najkrótszą linią na wejściu, to linia drukowana na wyjściu powinna być prawdopodobnie oryginalną wersją linii - a nie tą, którą zdezynfekowałem / zhomogenizowałem dla porównania. Potrzebuję więc dwóch wersji każdego z nich.
To niefortunne, że kolejną koniecznością jest wiele przełączeń buforów, aby poradzić sobie z tym samym - ale przynajmniej żaden bufor nigdy nie przekracza więcej niż czterech linii potrzebnych do utrzymania aktualności - a więc może nie jest straszny.
W każdym razie dla każdego cyklu pierwszą rzeczą, która się dzieje, jest transformacja zapamiętanej linii - ponieważ jedyną faktycznie zapisaną kopią jest dosłowny oryginał - w ...
^ \nremembered line$
... a następnie nlinia wejściowa ext zastępuje stary bufor. Jeśli nie zawiera co najmniej jednego znaku, jest skutecznie ignorowany. O wiele łatwiej byłoby po prostu skorzystać qz pierwszego pustego wiersza, ale cóż, moje dane testowe zawierały wiele takich i chciałem obsłużyć wiele akapitów.
I tak, jeśli zawiera znak, jego dosłowna wersja jest dołączana do zapamiętanej linii, a jego wersja porównawcza z odstępami jest umieszczana na początku przestrzeni wzorów, jak poniżej:
^ \n \nremembered line\nnew$
Na koniec stosuje się podstawienie do tej przestrzeni wzorów:
s/^\( *\)\(\n \1 *\)\{0,1\}\n//
Więc jeśli nowa linia zmieści się w przestrzeni potrzebnej do przechowywania zapamiętanej linii z co najmniej jednym znakiem do zaoszczędzenia, wówczas pierwsze dwie linie zostaną podstawione, w przeciwnym razie tylko pierwsza.
Niezależnie od wyniku, pierwsza linia w przestrzeni wzorów jest zawsze Dusuwana na końcu cyklu przed ponownym uruchomieniem. Oznacza to, że jeśli nowy wiersz jest krótszy niż ostatni ciąg ...
new
... jest odsyłany z powrotem do pierwszej substytucji w cyklu, która zawsze usuwa tylko pierwszy znak nowego wiersza - i dlatego pozostaje cała. Ale jeśli nie jest to ciąg ...
remembered line\nnew
... zamiast tego rozpocznie się następny cykl, a pierwsze podstawienie usunie z niego ciąg ...
\nnew
...każdego razu.
W ostatnim wierszu zapamiętana linia jest wypisywana na standardowe wyjście, więc dla podanych danych przykładowych wypisuje:
4for
Ale poważnie, użyj tr.