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 unexpand
zrobić:
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 sed
poradzi 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 s
in \1
i ''
łańcuchem zerowym w \2
.
1\nstring2\nstring3
... jest dopasowane 1
w \1
i \nstring2\nstring3
w\2
\nstring2\nstring3
... jest dopasowywane z \n
in \1
i ''
łańcuchem zerowym w \2
. Byłoby to problematyczne, gdyby istniała jakakolwiek szansa, że \n
ewline pojawi się na początku przestrzeni wzorów - ale polecenia /^\n/D
i //!g
sł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 \n
i s
ponownie do \1
i oba uzyskują ''
ciąg zerowy \2
. Puste linie w ogóle się nie zgadzają.
Kiedy wzór jest nakładany g
lobalnie, dwa odchylenia - zarówno odchylenie standardowe najbardziej lewe, jak i mniejsze \n
odchylenie 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 n
linia 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ć q
z 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 D
usuwana 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
.