p='[:punct:]' s='[:space:]'
sed -Ee'1!{/\n/!b' -e\} \
-e's/(\n*)(.*)/ \2 \1/' \
-e"s/is[$p]?[$s]/\n&/g" \
-e"s/([^$s])\n/\1/g;1G" \
-e:c -e"s/\ni(.* )\n{3}/u\1/" \
-e"/\n$/!s/\n//g;/\ni/G" \
-e's//i/;//tc' \
-e's/^ (.*) /\1/;P;$d;N;D'
Esa parte sed
simplemente lleva un recuento de is
ocurrencias de una línea a la siguiente. Debe manejar de manera confiable tantos is
es por línea como le arroje, y no necesita almacenar las líneas antiguas mientras lo hace; solo retiene un solo carácter de nueva línea por cada is
cosa que encuentra, que no es parte de otra palabra.
El resultado es que modificará solo la tercera aparición en un archivo, y llevará los recuentos por línea. Entonces, si un archivo se ve así:
1. is is isis
2. is does
... se imprimirá ...
1. is is isis
2. us does
Primero maneja los casos de borde insertando un espacio en la cabeza y la cola de cada línea. Esto hace que los límites de las palabras sean un poco más fáciles de determinar.
A continuación, busca valores válidos is
insertando una \n
línea de e antes de que todas las apariciones is
que preceden inmediatamente a cero o un signo de puntuación seguido de un espacio. Hace otra pasada y elimina todos los \n
ewlines que están precedidos inmediatamente por un carácter que no es espacio. Estos marcadores dejados coincidirán is.
y is
no, this
o no ?is
.
A continuación, reúne cada marcador en la cola de la cadena: para cada \ni
coincidencia en una línea, agrega una \n
línea de flecha a la cola de la cadena y la reemplaza con i
o u
. Si hay 3 \n
ewlines en una fila reunida en la cola de la cadena, entonces usa la u, de lo contrario, la i. La primera vez que se usa au también es la última: el reemplazo activa un bucle infinito que se reduce a get line, print line, get line, print line,
y así sucesivamente.
Al final de cada ciclo de bucle de prueba, limpia los espacios insertados, imprime solo hasta la primera línea nueva en el espacio del patrón y vuelve a funcionar.
Agregaré un l
comando ook en la parte superior del bucle como:
l; s/\ni(.* )\n{9}/u\1/...
... y eche un vistazo a lo que hace, ya que funciona con esta entrada:
hai this is linux.
hai this is unix.
hai this is mac.
hai this is unchanged is.
... así que esto es lo que hace:
hai this \nis linux. \n$ #behind the scenes
hai this is linux. #actually printed
hai this \nis unix. \n\n$ #it builds the marker string
hai this is unix.
\n\n\n$ #only for lines matching the
\n\n\n$ #pattern - and not otherwise.
hai this \nis mac. \n\n\n$ #here's the match - 3 ises so far in file.
hai this us mac. #printed
hai this is unchanged is. #no look here - this line is never evaled
Tiene más sentido quizás con más is
es por línea:
nthword()( p='[:punct:]' s='[:space:]'
sed -e '1!{/\n/!b' -e\} \
-e 's/\(\n*\)\(.*\)/ \2 \1/' \
-e "s/$1[$p]\{0,1\}[$s]/\n&/g" \
-e "s/\([^$s]\)\n/\1/g;1G;:c" \
-e "${dbg+l;}s/\n$1\(.* \)\n\{$3\}/$2\1/" \
-e '/\n$/!s/\n//g;/\n'"$1/G" \
-e "s//$1/;//tc" -e 's/^ \(.*\) /\1/' \
-e 'P;$d;N;D'
)
Eso es prácticamente lo mismo, pero escrito con POSIX BRE y manejo de argumentos rudimentarios.
printf 'is is. is? this is%.0s\n' {1..4} | nthword is us 12
... consigue ...
is is. is? this is
is is. is? this is
is is. is? this us
is is. is? this is
... y si habilito ${dbg}
:
printf 'is is. is? this is%.0s\n' {1..4} |
dbg=1 nthword is us 12
... podemos verlo iterar ...
\nis \nis. \nis? this \nis \n$
is \nis. \nis? this \nis \n\n$
is is. \nis? this \nis \n\n\n$
is is. is? this \nis \n\n\n\n$
is is. is? this is
\nis \nis. \nis? this \nis \n\n\n\n\n$
is \nis. \nis? this \nis \n\n\n\n\n\n$
is is. \nis? this \nis \n\n\n\n\n\n\n$
is is. is? this \nis \n\n\n\n\n\n\n\n$
is is. is? this is
\nis \nis. \nis? this \nis \n\n\n\n\n\n\n\n\n$
is \nis. \nis? this \nis \n\n\n\n\n\n\n\n\n\n$
is is. \nis? this \nis \n\n\n\n\n\n\n\n\n\n\n$
is is. is? this \nis \n\n\n\n\n\n\n\n\n\n\n\n$
is is. is? this us
is is. is? this is