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?
しかし、それに関する問題は、すべての行が機能するために長さが2倍以上でなければならないことです。そのため、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文字列in と一致します\2
。
1\nstring2\nstring3
... 1
in \1
および\nstring2\nstring3
in と一致\2
\nstring2\nstring3
... \n
in \1
および''
null文字列in と一致します\2
。\n
パターンスペースの先頭でewlineが発生する可能性がある場合、これは問題/^\n/D
になり//!g
ますが、これを防ぐために、およびコマンドが使用されます。私は使用しまし[^\n]
たが、この小さなスクリプトの他のニーズにより移植性が懸念され、多くの誤った解釈の方法に満足しませんでした。さらに、.
高速です。
\nstring2
string1
...に一致し\n
、s
再度入力する\1
と、両方で''
NULL文字列が取得され\2
ます。空の行はまったく一致しません。
パターンが部分的に適用されるg
と、2つのバイアス(左端の標準バイアスと右端の右の\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
基本的に、正規表現を使用して、適用するパターンスペースの最初の行のみを常に処理します。これにより、テストループに頼らずに、保持された最短一致と最新の両方のラインの2つの異なるバージョンをジャグリングできます。適用されたすべての置換は、パターンスペース全体を一度に処理します。
リテラル文字列/文字列比較には異なるバージョンが必要です。したがって、すべての文字が等しいことが保証されている各行のバージョンが必要です。しかし、もちろん、どちらかが実際に入力で最も早く発生する行である場合、出力に出力される行は、おそらく比較のためにサニタイズ/ホモジナイズしたものではなく、元のバージョンの行でなければなりません。したがって、それぞれ2つのバージョンが必要です。
別の必要性は、同じものを処理するために大量のバッファを切り替えることですが、少なくともどちらのバッファも現在の状態を維持するために必要な4行を超えることはありません。したがって、恐ろしいことではないでしょう。
とにかく、各サイクルで最初に発生するのは、記憶された行の変換です-実際に保存されるのはリテラルのオリジナルだけなので-に...
^ \nremembered line$
...その後、n
ext入力行は古いバッファを上書きします。少なくとも1つの文字が含まれていない場合、事実上無視されます。それははるかに簡単ですq
最初に出現する空白行でUIを実行するが、テストデータにはこれらの多くがあり、複数の段落を処理したかったのです。
そのため、文字が含まれている場合、そのリテラルバージョンは記憶されている行に追加され、その間隔比較バージョンは次のようにパターンスペースの先頭に配置されます。
^ \n \nremembered line\nnew$
最後に、そのパターンスペースに置換が適用されます。
s/^\( *\)\(\n \1 *\)\{0,1\}\n//
そのため、少なくとも1文字の予備の記憶された行を含むために必要なスペースに改行が収まる場合、最初の2行は置き換えられ、そうでない場合は最初の行のみが置き換えられます。
結果に関係なく、パターンスペースの最初の行は、D
サイクルの終わりに常に再起動される前に削除されます。これは、新しい行が最後の文字列より短い場合...
new
...サイクルの最初の置換に返送され、常に最初の改行文字からのみ削除されます。したがって、そのまま残ります。しかし、そうでない場合は文字列...
remembered line\nnew
...代わりに次のサイクルを開始し、最初の置換はそれから文字列を取り除きます...
\nnew
...毎回。
最後の行では、記憶された行が標準出力に出力されるため、指定されたデータ例では次のように出力されます。
4for
ただし、真剣に使用してくださいtr
。