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
... y el ganador es ... la línea 2, parece.
2:1111:4for
4:11111:five!
1:1111111:seven/7
3:11111111:8 eight?
Pero el problema con eso es que cada línea debe tener más del doble de longitud para que funcione, por lo que LINE_MAX se reduce a la mitad. La causa es que está utilizando, ¿qué, una base 1? - para representar la longitud de la línea. Un enfoque similar, y quizás más ordenado, podría ser comprimir esa información en la secuencia. La primera idea en ese sentido que se me ocurre es que debería unexpand
hacerlo:
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
Eso imprime ...
2
4for
Otro, solo 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
La sintaxis cumple con los estándares, pero eso no garantiza que haya sed
maneje \(reference-group\)\{counts\}
correctamente, muchos no lo hacen.
Básicamente, aplica la misma expresión regular a la entrada repetidamente, lo que puede ser muy beneficioso cuando es hora de compilarlos. Ese patrón es:
\(.\)\(\n.*\)*
Que combina diferentes cadenas de diferentes maneras. Por ejemplo:
string1\nstring2\nstring3
... coincide con s
in \1
y ''
la cadena nula en \2
.
1\nstring2\nstring3
... coincide con 1
in \1
y \nstring2\nstring3
in\2
\nstring2\nstring3
... coincide con \n
in \1
y ''
la cadena nula en \2
. Esto sería problemático si hubiera alguna posibilidad de que se \n
produzca una línea en la cabecera del espacio del patrón, pero los comandos /^\n/D
, y //!g
se utilizan para evitar esto. Utilicé [^\n]
pero otras necesidades para este pequeño script hicieron que la portabilidad fuera una preocupación y no estaba satisfecho con las muchas formas en que a menudo se malinterpreta. Además, .
es más rápido.
\nstring2
string1
... coinciden \n
y s
nuevamente \1
y ambos obtienen la ''
cadena nula \2
. Las líneas vacías no coinciden en absoluto.
Cuando el patrón se aplica de forma g
lobular, los dos sesgos, tanto el sesgo estándar más a la izquierda como el sesgo menor a la derecha del lado \n
derecho, se contrarrestan para efectuar un salto. Algunos ejemplos:
s/\(.\)\(\n.*\)*/\1:\2/g
s/\(.\)\(\n.*\)*/\2\1:/g
s/\(.\)\(\n.*\)*/\1: /g
s/\(.\)\(\n.*\)*/ :\2/g
... si todo se aplica (no en sucesión) a la siguiente cadena ...
string1\nstring2
... lo transformará a ...
s:t:r:i:n:g:1:\nstring2
s:t:r:i:n:g:\nstring21:
s:t:r:i:n:g:1:
: : : : : : :\nstring2
Básicamente, uso la expresión regular para manejar siempre solo la primera línea en cualquier espacio de patrón al que la aplique. Eso me permite hacer malabarismos con dos versiones diferentes de una línea retenida más corta hasta ahora y la línea más reciente sin recurrir a bucles de prueba: cada sustitución aplicada maneja todo el espacio de patrones a la vez.
Las diferentes versiones son necesarias para la comparación literal de cadenas / cadenas, por lo que debe haber una versión de cada línea donde se garantice que todos los caracteres sean iguales. Pero, por supuesto, si una u otra termina siendo la primera línea más corta en la entrada, entonces la línea impresa en la salida probablemente debería ser la versión original de la línea, no la que he desinfectado / homogeneizado por el bien de la comparación. Y entonces necesito dos versiones de cada uno.
Es desafortunado que otra necesidad sea una gran cantidad de cambio de búfer para manejar el mismo, pero al menos ninguno de los búferes excede nunca más de las cuatro líneas necesarias para mantenerse actualizado, por lo que tal vez no sea terrible.
De todos modos, para cada ciclo, lo primero que sucede es una transformación en la línea recordada, porque la única copia realmente guardada es el original literal, en ...
^ \nremembered line$
... y luego la n
línea de entrada ext sobrescribe cualquier búfer antiguo. Si no contiene al menos un solo carácter, se ignora efectivamente. Sería mucho más fácil simplementeq
pasar a la primera línea en blanco, pero, bueno, mis datos de prueba tenían muchos de esos y quería manejar múltiples párrafos.
Entonces, si contiene un carácter, su versión literal se agrega a la línea recordada y su versión de comparación espaciada se coloca en la cabecera del espacio del patrón, de esta manera:
^ \n \nremembered line\nnew$
Por último, se aplica una sustitución a ese espacio de patrón:
s/^\( *\)\(\n \1 *\)\{0,1\}\n//
Entonces, si la nueva línea puede caber dentro del espacio necesario para contener la línea recordada con al menos un carácter libre, entonces las dos primeras líneas se sustituyen, de lo contrario solo la primera.
Independientemente del resultado, la primera línea en el espacio del patrón siempre se D
elige al final del ciclo antes de comenzar de nuevo. Esto significa que si la nueva línea es más corta que la última, la cadena ...
new
... se envía de vuelta a la primera sustitución en el ciclo, que siempre se eliminará solo del primer carácter de nueva línea en adelante, por lo que permanece completo. Pero si no es así, la cadena ...
remembered line\nnew
... comenzará el siguiente ciclo, y la primera sustitución le quitará la cadena ...
\nnew
...cada vez.
En la última línea, la línea recordada se imprime con salida estándar y, por lo tanto, para los datos de ejemplo proporcionados, imprime:
4for
Pero, en serio, usa tr
.