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
... et le gagnant est ... ligne 2, semble-t-il.
2:1111:4for
4:11111:five!
1:1111111:seven/7
3:11111111:8 eight?
Mais le problème avec cela est que chaque ligne doit plus que doubler pour qu'elle fonctionne - donc LINE_MAX est effectivement réduit de moitié. La raison en est qu'il utilise - quoi, une base 1? - pour représenter la longueur de la ligne. Une approche similaire - et peut-être plus ordonnée - pourrait être de compresser ces informations en flux. La première idée dans ce sens qui me vient à l'esprit est que je devrais le faire 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
Cela imprime ...
2
4for
Un autre, juste 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 syntaxe est conforme aux normes - mais ce n'est pas une garantie que n'importe quel ancien sed
gérera \(reference-group\)\{counts\}
correctement - beaucoup ne le font pas.
Il applique essentiellement le même regexp à l'entrée à plusieurs reprises - ce qui peut être très bénéfique quand il est temps de les compiler. Ce schéma est:
\(.\)\(\n.*\)*
Qui correspond à différentes chaînes de différentes manières. Par exemple:
string1\nstring2\nstring3
... correspond à s
in \1
et à ''
la chaîne nulle in \2
.
1\nstring2\nstring3
... est associé à 1
in \1
and \nstring2\nstring3
in\2
\nstring2\nstring3
... correspond à \n
in \1
et à ''
la chaîne nulle in \2
. Cela serait problématique s'il y avait une chance qu'un \n
ewline se produise à la tête de l'espace de motif - mais les commandes /^\n/D
, et //!g
sont utilisées pour empêcher cela. J'ai utilisé, [^\n]
mais d'autres besoins pour ce petit script ont rendu la portabilité préoccupante et je n'étais pas satisfait des nombreuses façons dont il est souvent mal interprété. De plus, .
c'est plus rapide.
\nstring2
string1
... correspond \n
et s
encore \1
et les deux obtiennent la ''
chaîne nulle \2
. Les lignes vides ne correspondent pas du tout.
Lorsque le motif est appliqué g
lobalement, les deux biais - à la fois le biais standard le plus à gauche et le biais de la ligne droite le moins à droite \n
- sont contrebalancés pour effectuer un saut. Quelques exemples:
s/\(.\)\(\n.*\)*/\1:\2/g
s/\(.\)\(\n.*\)*/\2\1:/g
s/\(.\)\(\n.*\)*/\1: /g
s/\(.\)\(\n.*\)*/ :\2/g
... si tous s'appliquent (pas successivement) à la chaîne suivante ...
string1\nstring2
... le transformera en ...
s:t:r:i:n:g:1:\nstring2
s:t:r:i:n:g:\nstring21:
s:t:r:i:n:g:1:
: : : : : : :\nstring2
Fondamentalement, j'utilise l'expression rationnelle pour toujours gérer uniquement la première ligne dans n'importe quel espace de modèle auquel je l'applique. Cela me permet de jongler avec deux versions différentes d'une ligne conservée la plus courte jusqu'à présent et de la ligne la plus récente sans avoir recours à des boucles de test - chaque substitution appliquée gère tout l'espace de motif en même temps.
Les différentes versions sont nécessaires pour les comparaisons chaîne / chaîne littérales - il doit donc y avoir une version de chaque ligne où tous les caractères sont garantis égaux. Mais bien sûr, si l'un ou l'autre devait devenir la ligne la plus courte en entrée la plus ancienne, la ligne imprimée en sortie devrait probablement être la version originale de la ligne - pas celle que j'ai désinfectée / homogénéisée pour la comparaison. Et j'ai donc besoin de deux versions de chacune.
Il est malheureux qu'une autre nécessité soit beaucoup de commutation de tampons pour les gérer - mais au moins aucun tampon ne dépasse jamais plus que les quatre lignes nécessaires pour rester à jour - et donc ce n'est peut-être pas terrible.
Quoi qu'il en soit, pour chaque cycle, la première chose qui se produit est une transformation sur la ligne mémorisée - car la seule copie réellement enregistrée est l'original littéral - en ...
^ \nremembered line$
... et ensuite la n
ligne d'entrée ext écrase tout ancien tampon. S'il ne contient pas au moins un seul caractère, il est effectivement ignoré. Il serait beaucoup plus facile deq
la première ligne vierge, mais mes données de test en contenaient beaucoup et je voulais gérer plusieurs paragraphes.
Et donc s'il contient un caractère, sa version littérale est ajoutée à la ligne mémorisée et sa version de comparaison espacée est positionnée en tête de l'espace de motif, comme ceci:
^ \n \nremembered line\nnew$
Enfin, une substitution est appliquée à cet espace de motif:
s/^\( *\)\(\n \1 *\)\{0,1\}\n//
Donc, si la nouvelle ligne peut tenir dans l'espace nécessaire pour contenir la ligne mémorisée avec au moins un caractère à épargner, les deux premières lignes sont remplacées, sinon seulement la première.
Quel que soit le résultat, la première ligne de l'espace de motif est toujours D
supprimée à la fin du cycle avant de recommencer. Cela signifie que si la nouvelle ligne est plus courte que la dernière, la chaîne ...
new
... est renvoyé à la première substitution dans le cycle qui sera toujours supprimée uniquement à partir du premier caractère de nouvelle ligne - et il reste donc entier. Mais si ce n'est pas le cas, la chaîne ...
remembered line\nnew
... va commencer le cycle suivant à la place, et la première substitution en supprimera la chaîne ...
\nnew
...à chaque fois.
Sur la toute dernière ligne, la ligne mémorisée est imprimée en sortie standard, et donc pour les données d'exemple fournies, elle imprime:
4for
Mais, sérieusement, utilisez tr
.