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
... e o vencedor é ... a linha 2, ao que parece.
2:1111:4for
4:11111:five!
1:1111111:seven/7
3:11111111:8 eight?
Mas o problema é que todas as linhas devem ter mais do que o dobro de comprimento para que funcionem - portanto, LINE_MAX seja efetivamente dividido pela metade. A causa é que ele está usando - o que, uma base 1? - para representar o comprimento da linha. Uma abordagem semelhante - e talvez mais organizada - pode ser compactar essas informações no fluxo. A primeira ideia nesse sentido que me ocorre é que eu deveria 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
Isso imprime ...
2
4for
Outro, apenas 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
A sintaxe é compatível com os padrões - mas isso não garante que nenhum antigo sed
lidem com os problemas \(reference-group\)\{counts\}
corretamente - muitos não.
Basicamente, aplica o mesmo regexp à entrada repetidamente - o que pode ser muito benéfico quando é hora de compilá-las. Esse padrão é:
\(.\)\(\n.*\)*
Que corresponde a diferentes cadeias de maneiras diferentes. Por exemplo:
string1\nstring2\nstring3
... é correspondido com s
in \1
e ''
a cadeia nula em\2
.
1\nstring2\nstring3
... é combinado com 1
in \1
e \nstring2\nstring3
in\2
\nstring2\nstring3
... é correspondido com \n
in \1
e ''
a cadeia nula em \2
. Isso seria problemático se houvesse alguma chance de uma linha de \n
ew ocorrer no início do espaço do padrão - mas os comandos /^\n/D
e //!g
são usados para evitar isso. Usei, [^\n]
mas outras necessidades desse pequeno script tornaram a portabilidade uma preocupação e não fiquei satisfeito com as muitas maneiras pelas quais ele é mal interpretado. Além disso, .
é mais rápido.
\nstring2
string1
... correspondem \n
e s
novamente \1
e ambos obtêm a ''
cadeia nula \2
. Linhas vazias não coincidem.
Quando o padrão é aplicado g
globalmente, os dois desvios - o viés padrão mais à esquerda e o lado direito menor\n
ew do direito - são contrabalançados para efetuar um salto. Alguns exemplos:
s/\(.\)\(\n.*\)*/\1:\2/g
s/\(.\)\(\n.*\)*/\2\1:/g
s/\(.\)\(\n.*\)*/\1: /g
s/\(.\)\(\n.*\)*/ :\2/g
... se todos aplicados (não em sucessão) à seguinte sequência ...
string1\nstring2
... irá transformá-lo em ...
s:t:r:i:n:g:1:\nstring2
s:t:r:i:n:g:\nstring21:
s:t:r:i:n:g:1:
: : : : : : :\nstring2
Basicamente, eu uso o regexp para sempre manipular apenas a primeira linha em qualquer espaço de padrão ao qual eu o aplico. Isso me permite manipular duas versões diferentes de uma linha de correspondência mais curta retida até o momento e a linha mais recente sem recorrer a loops de teste - cada substituição aplicada lida com todo o espaço do padrão de uma só vez.
As versões diferentes são necessárias para comparações literais de string / string - portanto, deve haver uma versão de cada linha em que todos os caracteres sejam garantidos como iguais. Mas é claro que, se um ou outro deveria realmente ser a linha mais curta de entrada mais precoce, a linha impressa na saída provavelmente deveria ser a versão original da linha - e não a que eu higienizei / homogeneizei para fins de comparação. E então eu preciso de duas versões de cada.
É lamentável que outra necessidade seja muita troca de buffer para lidar com o mesmo - mas pelo menos nenhum buffer excede mais do que as quatro linhas necessárias para se manter atualizado - e, portanto, talvez não seja terrível.
De qualquer forma, para cada ciclo, a primeira coisa que acontece é uma transformação na linha lembrada - porque a única cópia realmente salva é o original literal - em ...
^ \nremembered line$
... e depois a n
linha de entrada ext substitui qualquer buffer antigo. Se não contiver pelo menos um único caractere, será efetivamente ignorado. Seria muito mais fácil apenasq
a primeira linha em branco, mas, bem, meus dados de teste tinham muitos deles e eu queria lidar com vários parágrafos.
E, se ele contém um caractere, sua versão literal é anexada à linha lembrada e sua versão de comparação espaçada é posicionada na cabeça do espaço do padrão, assim:
^ \n \nremembered line\nnew$
Por último, uma substituição é aplicada a esse espaço de padrão:
s/^\( *\)\(\n \1 *\)\{0,1\}\n//
Portanto, se a nova linha puder caber no espaço necessário para conter a linha lembrada com pelo menos um caractere de reposição, as duas primeiras linhas serão substituídas, senão somente a primeira.
Independentemente do resultado, a primeira linha no espaço do padrão é sempre D
excluída no final do ciclo antes de iniciar novamente. Isso significa que, se a nova linha for mais curta que a última, a string ...
new
... é enviado de volta à primeira substituição do ciclo, que sempre tira apenas o primeiro caractere de nova linha - e, portanto, permanece inteiro. Mas se não for, então a string ...
remembered line\nnew
... começará o próximo ciclo e a primeira substituição retirará a string ...
\nnew
...toda vez.
Na última linha, a linha lembrada é impressa para padronizar e, portanto, para os dados de exemplo fornecidos, ela imprime:
4for
Mas, sério, use tr
.