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 é ... linha 2, parece.
2:1111:4for
4:11111:five!
1:1111111:seven/7
3:11111111:8 eight?
Mas o problema é que cada linha deve mais que dobrar de tamanho para que funcione - então LINE_MAX é reduzido 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 arrumada - pode ser compactar essa informação no fluxo. A primeira ideia ao longo daquelas linhas que me ocorre é que eu devo unexpand
it:
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.*.*/!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.*\)*//g
$p;h; s// /g;G;x;n;//!g;H;s// /g
G; s/^\( *\)\(\n *\)\{0,1\}\n//
D' <infile >outfile
A sintaxe é compatível com os padrões - mas isso não é garantia de que qualquer sed
antigo manipulará o \(reference-group\)\{counts\}
corretamente - muitos não.
Basicamente, aplica-se o mesmo regexp à entrada repetidamente - o que pode ser muito benéfico quando é hora de compilá-los. Esse padrão é:
\(.\)\(\n.*\)*
Que corresponde cadeias diferentes de maneiras diferentes. Por exemplo:
string1\nstring2\nstring3
... corresponde a s
em
e ''
a string nula em
.
1\nstring2\nstring3
... corresponde a 1
em
e \nstring2\nstring3
em
\nstring2\nstring3
... corresponde a \n
em
e ''
a string nula em
. Isso seria problemático se houvesse alguma chance de um \n
ewline ocorrer na cabeça do espaço de padrão - mas os comandos /^\n/D
e //!g
são usados para evitar isso. Eu usei [^\n]
, mas outras necessidades para este pequeno script tornaram a portabilidade uma preocupação e eu não fiquei satisfeito com as muitas maneiras em que isso é muitas vezes mal interpretado. Além disso, .
é mais rápido.
\nstring2
string1
... corresponde \n
e s
novamente em
e ambos recebem a sequência ''
null em
. Linhas vazias não combinam de todo.
Quando o padrão é aplicado g
lobally , os dois vieses - o viés padrão mais à esquerda e o menor \n
ewline - são contrabalançados para efetuar um salto . Alguns exemplos:
s/\(.\)\(\n.*\)*/:/g
s/\(.\)\(\n.*\)*/:/g
s/\(.\)\(\n.*\)*/: /g
s/\(.\)\(\n.*\)*/ :/g
... se todas forem aplicadas (não em sucessão) à seguinte string ...
string1\nstring2
... irá transformá-lo para ...
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 somente a primeira linha em qualquer padrão de espaço ao qual eu aplico. Isso permite que eu faça malabarismos com duas versões diferentes de uma linha retida de correspondência mais curta até agora e a linha mais recente sem recorrer a loops de teste - cada substituição aplicada lida com todo o espaço de padrão de uma só vez.
As diferentes versões são necessárias para comparações literais de string / string - então deve haver uma versão de cada linha onde todos os caracteres sejam iguais. Mas, claro, se um ou outro deve ser realmente a linha mais curta que ocorre na entrada, então a linha impressa para a saída provavelmente deve ser a versão original da linha - não a que eu tenha higienizado / homogeneizado para comparação. E então eu preciso de duas versões de cada uma.
É lamentável que outra necessidade seja a 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 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 linha de entrada n
ext sobrescreve qualquer buffer antigo. Se não contiver pelo menos um único caractere, ele será efetivamente ignorado. Seria muito mais fácil apenas q
uit na 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 contiver um caractere, sua versão literal será anexada à linha lembrada e sua versão de comparação espaçada será posicionada no início do espaço padrão, assim:
^ \n \nremembered line\nnew$
Por último, uma substituição é aplicada a esse espaço padrão:
s/^\( *\)\(\n *\)\{0,1\}\n//
Portanto, se a nova linha puder caber dentro do espaço necessário para conter a linha lembrada com pelo menos um caractere de sobra, as duas primeiras linhas serão substituídas, senão somente a primeira.
Independentemente do resultado, a primeira linha no espaço padrão é sempre D
eletida no final do ciclo antes de iniciar novamente. Isso significa que, se a nova linha for menor que a última, a string ...
new
... é enviado de volta para a primeira substituição no ciclo, que sempre tira apenas do 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 removerá a string ...
\nnew
... todas as vezes.
Na última linha, a linha lembrada é impressa como padrão, e assim, para os dados de exemplo fornecidos, ela imprime:
4for
Mas, sério, use tr
.