TUTORIAL DE SED
---------------

ndice propuesto:

1. Ha odo hablar de las expresiones regulares?
2. Ejecucin y funcionamiento de sed
   2.1. Qu hace sed
   2.2. Sintaxis bsica
   2.3. Pattern space y hold space
   2.4. Ciclo normal de ejecucin
   2.5. Comandos
   2.6. Cmo alterar el ciclo de ejecucin
3. Enlaces recomendados
   3.1. Expresiones regulares
   3.2. sed
4. Guiones comentados
   4.1. Guiones en una lnea
   4.2. Extraccin de lneas
	4.3. Formateo
   4.4. Matemticas
   4.4. Emulacin de comandos de unix
          tac
			 wc -l
			 wc -c
          cat -n/cat -b
          unix2dos
          dos2unix

Tareas pendientes:

a) Escribir la seccin 2.5


1. Ha odo hablar de las expresiones regulares?

Sabe usted qu significa /^\(\w\+\) \1$/?

Si la respuesta es no, entonces le aconsejo que, antes de seguir leyendo, se
instruya en el uso de las expresiones regulares. Yo no las tratar aqu, pero
no se apure: hay muchsima informacin disponible en castellano. Consulte la
seccin de enlaces, si quiere ahorrarse el trabajo de buscar en google: creo
que es suficiente con lo que viene explicado en ellos.

2. Ejecucin y funcionamiento de sed

2.1. Qu hace sed

sed es un editor de flujo que toma dos datos de entrada: un flujo de entrada a
manipular (p.e. un fichero de texto) y el conjunto de rdenes (guin) que
describen la manipulacin. Como flujo de salida devuelve el flujo de entrada
modificado segn el guin de rdenes:

 -- flujo de entrada --,
                       |--> sed ---> flujo de salida modificado
 ------- guin --------'

Si no se especifica el fichero de entrada, sed entiende que se proporcionar a
travs de la entrada estndar (teclado). Del mismo modo, si no se direcciona
la salida, sed enviar el resultado a la salida estndar (pantalla).

Por ejemplo en:

$ sed 's/A-Z/a-z/' texto.txt

tenemos que

   flujo de entrada: fichero texto.txt
   guin de rdenes: 's/A-Z/a-z/', una sola orden en este caso

Traducida a romn paladino, la lnea quiere decir: toma el fichero texto.txt
(flujo de entrada), convirteme sus letras maysculas en minsculas (guin de
rdenes) mndame (imprmeme) el resultado a la pantalla.

2.2. Sintxis bsica 

Existen dos formas de indicar a sed cul es el guin de rdenes:

a) En lnea: sed [-e] 'rdenes-del-guin' fichero.txt

   Indicar el parmetro '-e' es optativo. Al menos en la versin GNU de sed,
   que es la que yo uso.
   
   'rdenes-del-guin' es una sucesin de rdenes de sed separadas por punto y
   coma (;). Por ejemplo,

   sed 'N;P;d' fichero.txt

   es un guin compuesto por tres rdenes: N, que aade al final del
   escritorio la siguiente lnea de fichero.txt; P, que imprime la primera
   lnea del escritorio; y d, que lo borra. El resultado de estas operaciones
   es un flujo de salida que devuelve las lneas impares del fichero. No se
   preocupe, si no entiende por qu es as: an no tiene suficientes elementos
   de juicio.

b) Mediante un fichero: sed -f guion.sed fichero.txt

   En este caso las rdenes de sed estn incluidas en un fichero denominado
   segn el ejemplo "guion.sed". Es equivalente a usar la forma anterior,
   aunque esta suele usarse cuando el guin es ms largo y su lectura en una
   lnea es complicada de entender o escribir. Si se usa el mismo ejemplo
   anterior, hacer un "cat" de guion.sed devuelve:

   ### Principio del script ###
   N                  # Aade otra lnea al escritorio
   P                  # Imprime la primera lnea del escritorio
   d                  # Borra escritorio e inicia un nuevo ciclo
   ###    Fin del script    ###
   
   El uso de la almohadilla (#) para indicar el inicio y el fin del guin y
   los comentarios no ha sido arbitrario: es el carcter para sealar los
   comentarios en sed.

   Si, por alguna razn, se desean escribir en una misma lnea dos rdenes
   distintas bastar separarlas como en el caso anterior mediante punto y coma
   (;).

Evidentemente, sed admite otros parmetros aparte de -e y -f. Si est
interesado en ellos consulte la pgina del manual o el archivo info.

Aparte de estas dos formas bsicas, la shell de bash (lo siento, es la nica
que conozco) nos permite hacer una variacin sobre la forma a) (guin en
lnea) y una variacin sobre la forma b) (guin en fichero). Tenga claro que
estas posibilidades no son exclusivas de sed, sino que son consecuencia de las
posibilidades que nos brinda bash. Por tanto, si hablsemos de awk o cualquier
otro comando de unix, podramos tambin hablar de ellas.

a bis) guin de rdenes en documento interno:

       sed -f - << "FIN" fichero.txt
       N
       P
       D
       FIN

       Es una forma de lograr escribir en lnea el guin con la claridad de
       separar en distintas lneas las rdenes. Advierta que he incluido el
       primer FIN entre comillas para evitar que bash realice alguna expansin
       (a "*","$", etc...) en el guin.
       
		 Es un mtodo muy til cuando se est escribiendo un guin de bash que
		 incluye una llamada a sed. Si el guin de sed incluido es pequeo,
		 bastar la forma a); pero si es largo y complicado de entender y
		 queremos tener todo reunido dentro del guin de bash, se puede echar
		 mano de este mtodo.
       
       Si tiene alguna duda, consulte la seccin Documentos Internos (Here
       Documents) de la pgina del manual de bash.

b bis) Invocacin automtica de sed gracias a la lnea de "shebang":

       Consiste simplemente en indicarle a bash dentro del propio fichero de
       guin cul es el programa que debe invocar. En nuestro caso:

       #!/bin/sed -f
       
       P
       N
       d

       Una vez hecho esto, le damos los permisos de ejecucin adecuados al
       fichero y podremos ejecutarlo directamente:

       $ ./guion.sed
       
2.3. Pattern space y hold space 

sed posee dos buffer en los que almacena informacin: el pattern space y el
hold space. Yo a partir de ahora los llamar (porque me da la gana de hacerlo
as) el escritorio y la gaveta: no es una traduccin literal del ingls; ni
siquiera una traduccin que haya ledo en algn sitio. Simplemente me parece
que representa muy bien la funcin de ambos. Si no te gusta, ya sabes:

sed 's/escritorio/pattern space/g;s/gaveta/hold space/g' tutorial.txt

Bueno, bueno... en realidad no lo sabes an, pero espero que acabes
sabindolo. ;-)

Concretando, como sus nombres indican, el escritorio es el espacio de trabajo
al que se trae el flujo de entrada para manipularlo (borrar, modificar,
imprimir, etc...) y la gaveta es un espacio auxiliar en que se pueden meter
datos que se prev usar ms adelante sacndolos al escritorio.

Esto es importante tenerlo presente, puesto que en sed el trabajo en el
escritorio es inmediato, es decir, del flujo de entrada solamente se puede
manipular aquello que tenemos en ese preciso momento en l y no lo que ya pas
por l o lo que an no ha pasado. 

2.4. Ciclo normal de manipulacin

sed divide el flujo de datos de entrada en lneas y a cada una de ellas le
aplica por separado el guin de rdenes. Por ejemplo, si tenemos un flujo
constituido por tres lneas

Esta es la primera
Esta es la segunda
Y esta la ltima

sed lo tratar en tres ciclos e ir consecutivamente aplicando el guin a cada
una de ellas. Por supuesto, eso significa que en el primer ciclo, tendremos en
el escritorio la lnea "Esta es la primera", en el segundo "Esta es la
segunda" y en el ltimo "Y esta la ltima". En principio (ya veremos que
existen mecanismos para complicar las cosas), si estamos en el segundo ciclo,
(es decir, con la lnea "Esta es la segunda" en el escritorio) no podremos
volver atrs a hacer algo que dejamos de hacer a la primera lnea "Esta es la
primera". Por el mismo principio, no podemos apoyarnos en algo que an este
por venir; por ejemplo, nos es imposible saber que estamos en la penltima
lnea del fichero, porque sed an no sabe las lneas que quedan por llegar.

En general, un ciclo se compone de:

1) Comienzo, en que sed lee la siguiente lnea del flujo de datos y la trae
   hasta el escritorio para que podamos manipularla.

2) Desarrollo, en que se aplica el guin al contenido del escritorio.

3) Final, en que se manda al flujo de salida lo que quede en el escritorio, se
	"tira a la basura" su contenido y se vuelve a comenzar un nuevo ciclo en
	caso de que queden ms lneas por leer.

Por supuesto, podemos actuar sobre este ciclo, leyendo lneas antes de
comenzar un nuevo ciclo o ingenindoselas para no tirar a la basura lo que
tenemos en nuestro escritorio, etc..., pero antes de hablar de ello es mejor
dar un repaso a los comandos.

2.5. Comandos

2.5.1 Lista de comandos [completar]

Esto va a ser una traduccin-resumen de lo referente a los comandos y su
sintaxis que se puede leer en las pginas info y man). Hasta que tenga tiempo
de escribirlo:

man sed
info sed

2.5.2 Ejecucin condicional de comandos

2.5.2.1 Ejecucin en una nica lnea

Basta con anteponer el nmero de lnea a la orden. Por ejemplo:

	sed '2d' fichero.txt

Aplica la orden d nicamente a la segunda lnea. El resultado es un flujo de
salida con todo el fichero.txt excepto la segunda lnea.

La ltima lnea del fichero se representa con un dlar ($).

2.5.2.2 Ejecucin en una lnea que contiene un patrn

Se antepone la expresin regular a la orden. Por ejemplo


	sed '/^ /d' fichero.txt

no manda al flujo de salida las lneas que empiezan por un espacio.

2.5.2.3 Ejecucin en un intervalo

Se separa el inicio y el fin del intervalo con una coma y a continuacin se
incluye la orden. Tanto el inicio como el fin pueden ser un nmero de lnea o
una expresin regular:

	1,5d
	1,/^$/b
	/^ /,40d
	/^A/,/\.$/p
	
2.5.2.4 Grupos de rdenes

Se incluyen entre parntesis ({}):

	sed '1,5{H;d};$G' fichero.txt

Este guin aplica las rdenes H y d a las cinco primeras lneas y la orden
G a la ltima.

2.5.2.5 No ejecucin

Esto es, ejecuta la orden excepto para tal lnea o tal intervalo: se interpone
un cierre de exclamacin (!) entre la lnea/intervalo y la orden. Ejemplos:

	2!d        ----> Borra la lnea excepto si es la segunda
	/^ /!d     ----> Borra la lnea excepto si empieza con espacio
	1,5!d      ----> Borra la lnea excepto si est en el intervalo 1-5
	1,5!{H;d}  ----> Aplica el grupo de rdenes excepto a las lneas 1-5

2.6. Cmo alterar el ciclo de ejecucin

Ya se ha explicado cul es el ciclo de ejecucin de sed: traer al escritorio
una lnea, cacharrear con ella, imprimirla, tirarla y volver a hacer lo mismo
con la siguiente hasta que no queden ms lneas por leer. Sin embargo, este
comportamiento normal puede alterarse mediante:

1) Opcin -n: simplemente no imprime el contenido del escritorio al final del
   ciclo, es decir:

   $ sed '' fichero.txt

   imprime todas las lneas del fichero tal cual (ya que no indicamos ninguna
	orden a sed), pero

   $ sed -n '' fichero.txt

   no imprime nada. Digamos que esta opcin nos vuelve mudito a nuestro sed.
   Si no le indicamos que imprima expresamente algo (p o P), no lo har.

2) Etiquetas, que en combinacin con las rdenes b,t y T, impiden que el ciclo
	acabe y empiece el siguiente. Si eso ocurre, no suceder nada de lo asociado
   al final y al comienzo de un ciclo (impresin, borrado y lectura de nueva
   lnea). Por ejemplo:

   $ sed ':loop;b loop' fichero.txt

   Esta ejecucin empezar el primer ciclo (o sea, leer la primera lnea de
	fichero.txt y la traer al escritorio). A continuacin, se encontrar con un
   salto incondicional a la etiqueta ":loop", as que saltar hacia adelante;
   volver a encontrarse con el salto y volver hacia atrs y as ad infinitum...
   Consecuencia: acabamos de crear un lindo bucle infinito que nos impedir
   leer alguna vez la segunda lnea.

3) rden d: Esta orden borra todo el escritorio y comienza un nuevo ciclo de
   forma normal aunque an queden rdenes por ejecutarse.

5) rden D: Borra slo la primera lnea del escritorio. Si el escritorio
   se queda vaco empieza un ciclo normalmente (lee la siguiente lnea del
   fichero). Si no est vaco, empieza un ciclo sin leer la siguiente
   lnea. Por ejemplo:

	$ sed 'G;D' fichero.txt

	Este programa se ejecutar eternamente sin que jams se llegue a leer la
	segunda lnea del fichero. La razn es que G aade al final del escritorio
	lo que hay en la gaveta (si no se guard previamente nada, una lnea en
	blanco). Por ello, al ejecutar "D", el escritorio siempre contendr dos
	lneas, la primera de las cuales ser borrada. Como seguir habiendo una,
	no se leer la siguiente lnea de fichero.txt, sino que se seguir
	trabajando con la lnea que queda. Pero G volver a aadir una segunda
	lnea, lo que impedir a "D" lograr vaciar completamente el escritorio.
	Consecuencia: otro lindo bucle infinito.

6) rdenes n/N: nos permiten leer la siguiente lnea sin esperar al fin del
	ciclo. La primera reemplaza el contenido del escritorio, la segunda aade
	la nueva lnea al final.

3. Enlaces recomendados

3.1. Expresiones regulares

Artculo de LinuxFocus que sirve a modo de introduccin:
   http://www.linuxfocus.org/Castellano/July1998/article53.html

Tutorial bastante extenso en bulma:
   http://bulma.net/impresion.phtml?nIdNoticia=770

3.2. sed

La pgina info de sed (la del manual es bastante escueta):
	info sed

sed trabajando en una sola lnea: coleccin de manipulaciones que sed puede
hacer con guiones de una sola lnea:
   http://www.student.northpark.edu/pemente/sed/sed1line.txt

Tutorial de sed: sencillamente delicioso. Es el documento con el que he
comprendido exactamente cmo funciona sed. Contiene una pequea introduccin a
sed con la filosofa de su funcionamiento y la explicacin de sus comandos.
Adems incluye multitud de guiones, ms o menos extensos, profusamente
comentados. Su didctica es la que pretende imitar este tutorial, que toma
de l muchos de los ejemplos.

4. Guiones comentados

4.1. Guiones en una lnea

* Mostrar la lnea octava del fichero

	Borramos cualquier lnea que no sea la octava. Adems tras imprimirla,
	salimos de sed, puesto que el resto del fichero no nos interesa en
	absoluto.

		sed '8!d;q' fichero.txt

	Obsrvese que no hace falta especificar a qu lnea queremos aplicar
	q, porque "d" (que se ejecuta para las siete primeras lneas) aborta
	el resto del guin e inicia un nuevo ciclo.

* Borrar desde la lnea 3 a la 6 del fichero:

	Borramos el intervalo de lneas entre la 3 y la 6

		sed '3,6d' fichero.txt

* Mostrar entre la lnea 3 y la 6 del fichero

	Borramos cualquier lnea que no se encuentre en ese intervalo. Tras
	imprimir la sexta salimos, puesto que el resto del fichero no nos
	interesa en absoluto.

		sed '3,6!d;6q' fichero.txt

	En este caso, s es necesario indicar la lnea en que se quiere
	ejecutar "q", puesto que las lneas 3,4 y 5 no ejecutan d y, por
	tanto, continan la ejecucin normal del bucle.

* Aadir una lnea en blanco despus de cada prrafo.

	Vamos a considerar que un fin de prrafo se produce cuando hay una
	lnea que acaba en punto (.).

	El mtodo es muy simple: cada vez que encontramos una lnea que acaba
	en punto aadimos al escritorio una lnea en blanco. Para esto,
	simplemente usamos el comando "G", puesto que si no se ha almacenado
	nada, la gaveta contiene una lnea vaca.

		sed '$q;/\.$/G' fichero.txt

* Imprimir las lneas impares de un fichero.

	En este caso el mtodo consiste en leer traer lneas en cada ciclo, pero
   slo imprimir la primera.

   sed -n '$!N;P' fichero.txt

   Como se ha indicado la opcin '-n', no hace falta borrar el escritorio
   antes de que acabe el ciclo.

   Otra alternativa es:

   sed '$!N;s/\n.*$//' fichero.txt

   En este caso, la manera de evitar que se devuelva la lnea par es
   hacindola desaparecer mediante una sustitucin.

* Imprimir las lneas pares de un fichero.

  El mtodo equivalente al primero que se sugiri para el ejercicio anterior
  puede ser:

  sed -n '1!{$!N;P}' fichero.txt

  que es exactamente lo mismo, excepto por el hecho de que en la primera lnea
  evitamos juntar con la siguiente. Como consecuencia, en el resto de ciclos
  las parejas de lneas sern 2-3, 4-5, 6-7, etc... Como de cada pareja
  imprimimos la primera, el resultado es que mostramos las lneas pares.

  Y el equivalente al segundo:

  sed '$!N;s/[^\n]*\n\?/' fichero.txt


4.2. Extraccin de lneas

* Mostrar las seis ltimas lneas de un fichero:

	Por su forma de trabajar, sed no puede hacer pronsticos sobre cuando
   acabar el fichero. Por ello, es imposible usar expresiones del tipo $-6 y la
   nica forma de resolver el problema planteado es mantener en el escritorio las
   seis ltimas lneas ledas del fichero y, cuando lleguemos a la ltima,
   imprimir lo que haya en l.

	Vamos a plantear cuatro posibles soluciones. Por supuesto, todas se
	basan en la idea que se ha expresado arriba. La utilidad de revisarlas
	todas es que nos permite razonar sobre la forma en que acta sed.

	Solucin 1:

      : loop         #1
      1,5 {          #2 
            N        #3
            b loop   #4
      }              #5
      $! {           #6
            N        #7
            D        #8
      }              #9

	Las rdenes #1-#5 crean un bucle que nos permite almacenar las seis
	primeras lneas del fichero en el escritorio. A partir de la sptima
	lnea, se ejecuta el bloque #6-#9 que va eliminando la primera de
	las lneas almacenadas en el escritorio (la ms antigua leda) y
	aadiendo al final la ltima leda. Como consecuencia, cuando leamos la
	ltima, el escritorio contendr las seis ltimas y no habr que
	ejecutar ninguna orden, sino procurar que el ciclo acabe normalmente
	(impresin del escritorio).

	Solucin 2:

	Si nos fijamos en el guin anterior, en todos los ciclos se ejecuta
	una N, as que vamos a dar otra solucin en que la orden N sea comn a
	los dos bloques:

		:loop      #1
		N          #2
		2,5b loop  #3
		$!D        #4

   Sea cual sea la lnea leda, aadimos al escritorio la siguiente (#2). Si
   hemos ledo alguna de las cinco primeras lneas, volvemos a aadir sin
   borrar nada (#3). Como consecuencia, la ltima vez que se ejecute #3,
   acabaremos con las seis primeras lneas del fichero almacenadas en el
   escritorio. A partir de ese momento, #4 borra la primera lnea e inicia un
   nuevo ciclo con cinco lneas en el escritorio. Cuando nos encontremos en la
   lectura de la penltima lnea, N aadir la ltima. Sin embargo, en ella #4
   no se ejecutar. Por tanto, acabar el ltimo ciclo de forma normal, esto
   es, imprimiendo las seis ltimas lneas del fichero.
	
	Solucin 3:

		:loop        #1
		$q           #2
		N            #3
		2,6b loop    #4
		D            #5

	Muy parecida a la anterior. La nica diferencia es que en este caso la
	ltima vez que se ejecute N (penltima lnea), tambin se ejecuta D
	(ltima lnea). Por ello, no debemos almacenar seis lneas
	consecutivas, sino siete; que sern reducidas a seis cada vez que
	pasemos por D.

   Quizs es aconsejable explicar el final del proceso: en el penltimo ciclo
   nos encontraremos en la penltima lnea, #3 aadir la ltima lnea y #5
   borrar la primera de las lneas almacenadas (es decir, la sptima del
   fichero empezando a contar por el final). El ltimo ciclo, simplemente,
   ejecuta #2, que imprime el escritorio y sale de sed.

	Solucin 4:
	
	En ella en vez de expresar el primer grupo de lneas, expresamos el
	segundo. Nos vamos a basar en la solucin 3:

		:loop        #1
		$q           #2
		N            #3
		7,$D         #4
		b loop       #5
	
	No es necesario hacer ningn comentario, no?

* Borrar las ltimas seis lneas

   Como sed no sabe lo que an queda por leer del fichero, la nica forma de
   hacer esto es imprimir con seis lneas de retraso: cuando se va por la
   lnea 7, imprimir la 1 y, por tanto, cuando se va por la ltima lnea,
   imprimir la sptima por la cola y salir.

   As que el problema es muy semejante al anterior: lograr reunir siete
   lneas en el escritorio y, antes de borrar la primera de ellas, imprimirla.
   En vez de un $q, se hace un $Q para desechar lo que queda en el pattern
   space (las ltimas seis lneas). Tomemos la ltima de las soluciones del
   ejemplo anterior:

      :loop       #1
      $Q          #2
      N           #3
      7,$ {       #4
            P     #5
            D     #6
      }           #7
      b loop      #8

   Por supuesto, se puede tomar cualquiera. Tomar la penltima nos ahorra los
   parntesis:

      :loop       #1
      $Q          #2
      N           #3
      2,6b loop   #4
      P           #5
      D           #6

* Devolver toda lnea que contengan /patrn/ y la lnea que
  inmediatamente preceda a cada una:

   La forma de razonar es idntica a la de otros guiones: como sed no puede
   predecir qu contendr la siguiente lnea, es necesario tener dos lneas en
   el escritorio.

      /patrn/b         #1
      $!N               #2
      /\n.*patrn/!D    #3

   En caso de que leamos una lnea que contenga "patrn", simplemente la
   imprimimos y pasamos a la siguiente (#1). Si no lo contiene, ser necesario
   aadir la siguiente lnea (#2) y comprobar (#3) si est contiene el
   "patrn". Si no es as, borramos la primera lnea, conservamos la segunda
   (#3) y comenzamos ciclo con ella; si s, acabamos ciclo normalmente para
   imprimir las dos lneas.

* Devolver toda lnea que contenga /patrn/, las inmediatamente anterior y
  posterior

	El algoritmo se basa en acumular lneas en el escritorio (siempre que se
	haya encontrado una lnea que contenga /patrn/) hasta que la ltima no lo
	contenga:
	
		: loop              #1
		$!N                 #2
		/patrn/!D          #3
		/patrn[^\n]*$/!b   #4
		$!b loop            #5

	#2 junta la lnea actual con la siguiente. Si en el escritorio no se
	encuentra /patrn/, #3 borra la primera lnea y regresa a #2 para aadir
	una nueva.  En caso de que s contenga /patrn/, #4 comprueba que la ltima
	lnea no lo contiene y en tal caso acaba el ciclo normalmente (impresin y
	borrado del escritorio). En caso contrario, es necesario seguir aadiendo
	lneas al escritorio (#5), a menos que se haya llegado al final del
	fichero.

* Borrar todo el contenido comprendido entre /inicio/ y /fin/

  Por supuesto, no se supone que ambos patrones se encuentran en una misma
  lnea, lo que convertira el problema en trivial.

  En este caso, la forma de actuar es almacenar lneas en el escritorio a
  partir del momento en que hemos encontrado /inicio/ hasta que hallemos
  /fin/.
  
		/inicio/!b       #1
		: loop           #2
		$!N              #3
		s/inicio.*fin//  #4
		$!T loop         #5

	#1 acaba el ciclo normalmente si el escritorio no contiene /inicio/. En
	caso contrario se entra en un bucle (#2-#5) por el que se van sucesivamente
	aadiendo lneas al escritorio (#3). El bucle acaba, bien porque se ha
	llegado al final del fichero, bien porque se ha logrado (#5) borrar el
	contenido entre /inicio/ y /fin/ (#4). En ambos casos se acaba normalmente
	el ciclo.

	Ojo! T es una extensin GNU de sed, as que si se usa otro sed es posible
	que no funcione. No obstante, la misma idea puede plasmarse sin usar T:

		/inicio/!b      #1
		: loop          #2
		$!N             #3
		s/inicio.*fin// #4
		t               #5
		$!b loop        #6
	
	Es decir, si la sustitucin tiene xito, acabamos ciclo (#5) saltando #6;
	si no, regresamos a #3.

* Imprimir la ltima lnea de un grupo de lneas consecutivas que contienen
todas un mismo campo inicial

	Basta con llevar siempre dos lneas en el escritorio y, en caso, de que los
	campos iniciales de ambas no coincidan, imprimir la primera.

		$b                       #1
		N                        #2
		/^\([^ ]* \).*\n\1/!P    #3
		D                        #4

	#1 acaba ciclo en caso de que estemos en la ltima lnea. En caso
	contrario, #2 aade la siguiente lnea, #3 imprime la primera lnea en caso
	de que los campos iniciales sean distintos y #4 la borra y comienza ciclo
	con la segunda lnea.

4.3. Formateo

* Extraer la cabecera de un correo, haciendo que cada campo (From, To,
  etc...) ocupe una sola lnea

  A veces es necesario manipular cabeceras de correo. El problema es que es
  muy comn que las cabeceras ocupen varias lneas; de modo que, si por ejemplo
  se quieren extraer todos los destinatarios de un mensaje, no basta con
  manipular la lnea que contiene en patrn /^To: /.

  Por supuesto, se pueden usar utilidades como formail para remediar este
  problema, pero vamos a intentar resolverlo todo con sed.

  La estrategia es aadir lneas al escritorio hasta que nos topemos con un
  nuevo campo. En ese momento, se imprime el que se ha completado y se
  comienza a completar el nuevo.

		/^$/Q                             #1
		: loop                            #2
		N                                 #3
		/\n[ \t][^\n]*$/b loop            #4
		s/\n[ \t]\+/ /g                   #5
		P                                 #6
		D                                 #7

	El grupo #2-#4 es un bucle que aade lneas al escritorio hasta que sed
	encuentre una que no empiece por caracteres blancos, seal de que empieza
	otro campo o se ha llegado al final de la cabecera. Al salir del bucle #5
	elimina todos los cambios de lnea, excepto el ltimo; #6 imprime la
	primera lnea (el campo completado) y #7 la borra conservando la segunda e
	iniciando un nuevo ciclo. Si esta segunda lnea es una lnea en blanco (fin
	de la cabecera), acabamos el proceso (#1).
	
	Obsrvese que si lo que se quiere es extraer un campo en concreto, basta
	con condicionar la orden P. Por ejemplo, /^\(From\|Cc\): /P imprimira los
	campos de remitente y copia de carbn.

* Separar con una lnea en blanco grupos de lneas consecutivos que
  contienen todas un mismo campo inicial

		$q                                 #1
		N                                  #2
		P                                  #3
		/^\([^ ]* \).*\n\1/!i\             #4

		D                                  #5

	#2 aade la lnea siguiente, #3 imprime la primera lnea del escritorio y,
	si la segunda lnea empieza con distinto campo, #4 incluye en el flujo de
	salida una lnea en blanco. En cualquier caso, #5 borra la primera lnea y
	comienza un nuevo ciclo con la segunda.

* Fusionar lneas hasta que haya una que acabe en punto (.):

		: loop           #1
		/\.$/b           #2
		N                #3
		y/\
		/ /              #4
		$!b loop         #5

	Si el contenido del escritorio acaba en punto, se acaba el ciclo (#2). Si
	no es as, #3 aade la siguiente lnea y #4 elimina el cambio de lnea. A
	menos que hayamos llegado al final, #5 vuelve a #2 sin alterar el
	escritorio.

* Separar prrafos y sangrar (se supone que un prrafo acaba cuando nos
encontramos una lnea que acaba en punto)

	Como se debe actuar sobre la lnea siguiente a la que contiene un punto, es
	necesario tener dos lneas en el escritorio:

		/\.$/!b           #1
		$q                #2
		p                 #3
		i\                #4
		
		N                 #5
		s/[^\n]*$/\t&/    #6
		D                 #7

	#1 imprime la lnea sin ms en caso de que no se trate un final de prrafo.
	En caso contrario, #2 comprueba si es la ltima, en cuyo caso la accin se
	reduce tambin a imprimir. A partir de #2, se ejecuta el algoritmo para
	incluir una lnea en blanco: #3 imprime, #4 incluye la lnea en blanco, #5
	aade la siguiente lnea y #6 le realiza el sangrado. Como remate, #7 borra
	la primera lnea (que ya se imprimi) y se inicia un nuevo ciclo con la
	segunda.

4.4. Matemticas

* Dado un fichero en que cada lnea es un nmero, sumar una unidad a cada
uno de ellos.

	sed no tiene ningn comando para realizar sumas, de modo que para poder
	realizar esto, se tiene que estudiar cmo cambia un nmero al que se le
	suma una unidad y realizar las sustituciones pertinentes.

	Si le sumamos una unidad:

	a) Un nmero que est constituido nicamente por nueves, aade una cifra
	   ms a su izquierda: 999 (3 cifras) ---> 1000 (4 cifras).

	b) Un nmero puede considerarse constituido por una parte variante y una
		parte invariante. La parte variante est constituida por todos los
		nueves que hay ms a su derecha y la cifra inmediatamente adyacente a la
		izquierda de este grupo de nueves. Por ejemplo, en el caso 1269 --->
		1270. La parte variante es 69 y la invariante 12. En el caso particular
		de que no haya nueves, podemos considerar que el grupo de nueves est
		constituido por 0 nueves, de modo que la parte variante es nicamente el
		nmero que hay ms a la derecha: 1234 ---> 1235. Parte variante, 5;
		parte invariante, 123. As pues, si usamos como separador "x" de ambas
		parte, algunos ejemplos son:

		1999   ----> x1999
		12     ----> 1x2
		1289   ----> 12x89
		999    ----> 0999 (aplicando a) primero) ----> x0999

		Expresado en la sintaxis de sed la parte variante es /.9*$/.
	
	c) Las cifras que varan lo hacen de modo que 0->1, 1->2, ..., 0->9.
	   Expresado en la sintaxis de sed: y/0123456789/1234567890/.
	
	Con todo esto ya se puede construir el algoritmo:

		s/^9*$/0&/               #1
		s/.9*$/x&/               #2
		h                        #3
		s/*x//                   #4
		y/0123456789/1234567890/ #5
		x                        #6
		s/x*//                   #7
		G                        #8
		s/\n//                   #9
	
	#1 aade un cero delante los nmeros constituidos nicamente por nueves. #2
	separa la parte variante de la invariante mediante una "x". Ahora es
	necesario cambiar la parte variante. Sin embargo, como el comando y es
	indiscriminado y no se le puede pedir que altere slo a partir de la "x",
	no nos queda ms remedio que: meter una copia en la gaveta (#3), eliminar
	la parte invariante (#4), hacer la variacin (#5), intercambiar los
	contenidos del escritorio y la gaveta (#6), eliminar la parte variante
	(#7), aadir al escritorio la gaveta (#8) y, dado que la primera lnea
	contiene la parte invariante y la segunda la variante ya variada, eliminar
	el retorno de carro (#9).

* Trasponer una matriz que viene dada en un fichero de la forma:

  11 12 13
  21 22 23
  31 32 33

  Algoritmo:

		: inicio                           #1
		$! {                               #2
		     N                             #3
			  b inicio                      #4
		}                                  #5
		s/^/\n/                            #6
		: fila                             #7
		s/$/\a/                            #8
		: columna                          #9
		s/\n\(\w\+\)[ \t]*\(.*\)$/|\2\1 /  #10
		t columna                          #11
		y/|/\
		/                                  #12
		s/^\n*\a//                         #13
		t fin                              #14
		b fila                             #15
		: fin                              #16
		s/ *\a */\n/g                      #17

	
	El bucle #1-#6 trae al escritorio todas las lneas del fichero. Una vez que
	se tiene toda la matriz, #8 aade al final el carcter "\a" que va a servir
	para separar la siguiente fila de la matriz traspuesta. El bucle #9-#10
	coloca la columna n-sima de elementos tras el ltimo "\a" y separados por
	espacios. A la vez va sustituyendo los cambios de lnea "\n" por el
	carcter "|". Cuando ya no queden ms "\n" significar que la columna se ha
	completado y saldremos del bucle. #12 restituye los "\n" y #13 intenta
	sustituir una cadena de "\n" desde el inicio del escritorio al primer "\a".
	Si no lo consigue es seal de que quedan ms columnas en la matriz y se ha
	de comenzar de nuevo (#16). En caso contrario, #15 nos lleva al final #17,
	en que se sustituyen los "\a" por cambios de lnea

	El algoritmo tiene una limitacin: no se puede usar "|" en los elementos de
	la matriz, ya que es usado como carcter de control. Se usa "|" y no
	cualquier otro carcter de control (por ejemplo, "\r") porque la orden "y"
	de sed no permite usarlos :(. Se puede estar tentado de usar "s" en vez de
	"y" en #12 para evitar este problema. Sin embargo, esta "s", alterara el
	funcionamiento de la condicin #14. Lo que s puede hacerse es desplazar la
	sustitucin hasta despus de #14. Por ejemplo:


		: inicio                            #1
		$! {                                #2
		     N                              #3
			  b inicio                       #4
		}                                   #5
		s/^/\n/                             #6
		: fila                              #7
		s/$/\a/                             #8
		: columna                           #9
		s/\n\(\w\+\)[ \t]*\(.*\)$/\r\2\1 /  #10
		t columna                           #11
		s/^\r*\a//                          #12
		t fin                               #13  <--------------------------.                        
		s/\r/\n/g                           #14  <--- "s" pero despus de --'
		b fila                              #15
		: fin                               #16
		s/ *\a */\n/g                       #17

* Hacer las sumas en ficheros con formato:

   num1+num2+num3
	num4+num5
	num6+num7+num8+num9
	.
	.
	.

	Pues s, con un poco de imaginacin, hasta se pueden sumar nmeros! El
	truco consiste en agrupar las unidades, decenas, centenas, etc... de cada
	nmero por lneas, convertir los nmeros en cuentas de un baco, operar con
	el baco y, al final, traducir el nmero expresado en el baco a base
	decimal. Por ejemplo, supongamos que recibimos la suma "34+577+1":

	a) Agrupar unidades, decenas, etc... por lneas:

		471
		37
		5

   b) Convertir los nmeros en cuentas de un baco:

		oooooooooooo          <-- 4+7+1 'o'
		oooooooooo            <-- 3+7   'o'
		ooooo                 <--   5   'o'

	c) Operar con el baco, es decir, reordenar las cuentas de modo que el
		baco represente un nmero en base decimal (no puede haber ms de diez
		cuentas por lnea):

		oo                    <-- 2
		o                     <-- 1
		oooooo                <-- 6
	
	d) Convertir el nmero expresado en el baco en base decimal:

		612

	Ah va el algoritmo:

		s/$/+/                          #1    -+-
		: x10                           #2     |
		s/$/\n/                         #3     |
		: siguiente                     #4     |
		s/\([0-9]\)\?+\(.*\)$/-\2\1/    #5     |
		t siguiente                     #6     |  Labor a)
		y/-/+/                          #7     |
		s/^+*\n//                       #8     |
		t numero-abaco                  #9     |
		b x10                           #10    |
		: numero-abaco                  #11   -+-
		s/+\n//                         #12    |
		s/0//g                          #13    |
		s/1/o/g                         #14    |
		s/2/oo/g                        #15    |
		s/3/ooo/g                       #16    |
		s/4/oooo/g                      #17    |  Labor b)
		s/5/ooooo/g                     #18    |
		s/6/oooooo/g                    #19    |
		s/7/ooooooo/g                   #20    |
		s/8/oooooooo/g                  #21    |
		s/9/ooooooooo/g                 #22    |
		: ordenar                       #23   -+-
		s/^\(.*\)o\{10\}\n\?/\1\no/     #24    |  Labor c)
		t ordenar                       #25   -+-
		s/$/\n/                         #26    |
		: abaco-numero                  #27    |
		s/^\n\([^0-9]*\)/\10/           #28    |
		s/^o\{1\}\n\([^0-9]*\)/\11/     #29    |
		s/^o\{2\}\n\([^0-9]*\)/\12/     #30    |
		s/^o\{3\}\n\([^0-9]*\)/\13/     #31    |
		s/^o\{4\}\n\([^0-9]*\)/\14/     #32    |  Labor d)
		s/^o\{5\}\n\([^0-9]*\)/\15/     #33    |
		s/^o\{6\}\n\([^0-9]*\)/\16/     #34    |
		s/^o\{7\}\n\([^0-9]*\)/\17/     #35    |
		s/^o\{8\}\n\([^0-9]*\)/\18/     #36    |
		s/^o\{9\}\n\([^0-9]*\)/\19/     #37    |
		/^[0-9]/!b abaco-numero         #38   -+-

	#1-#10 realizan la labor a): #1 aade un signo '+' al final de la lnea, #3
	aade una nueva lnea al escritorio, el bucle #4-#6 extrae las cifras que
	anteceden a los signos '+' y las incluye en la ltima lnea que abrimos con
	#3. As pues, la primera vez que completemos este bucle la ltima lnea
	contendr las unidades de todos los nmeros. Como consecuencia del bucle
	los signos '+' habrn pasado a convertirse a '-'. Para subsanarlo se usa
	#7. #8 intenta eliminar la primera lnea suponiendo que todo son signos '+'
	lo que significara que todas las cifras de los nmeros ya han sido
	agrupadas correctamente y debemos continuar el al algoritmo (#9). Si la
	sustitucin no ha tenido xito, con #10 volvemos a #3 para aadir una nueva
	lnea y repetir el proceso.
	
	#11-#22 realizan la labor b): cada cifra se sustituye por un nmero de oes
	igual al nmero que representa (1->'o',2->'oo', etc.). 
	
	#23-#25 realizan la labor c): encontrar 10 cuentas en una lnea supone
	sustituirlas por una cuenta en la lnea inmediatamente inferior. El bucle
	continuar hasta que no queden grupos de 10 cuentas.
	
	#26-#38 realizan la labor d): #26 abre una lnea al final del escritorio
	para ir almacenando el resultado, #28-#37 sustituyen las cuentas por su
	equivalente en nmero ('' -> 0, 'o' -> 1, etc.). Mientras no queden slo
	nmeros en el escritorio, #38 nos devuelve al inicio del bucle para seguir
	convirtiendo cuentas.

4.5. Emulacin de comandos unix

* tac (invierte el orden de las lneas de un fichero)

		1!G  #1
		$q   #2
		h    #3
		d    #4

	#1 aade saca el contenido de la gaveta al final del escritorio. Con esto
	logramos que la nueva lnea est antes que las antiguas. Si hemos llegado a
	la ltima lnea, acabamos imprimiendo (#2), en caso contrario guardamos el
	escritorio en la gaveta (#3) y comenzamos ciclo sin imprimir (#4). Si se
	usa la opcin -n en la invocacin de sed, podramos ahorrarnos una lnea.

* wc -l (contar las lneas del fichero)

	sed -n '$=' fichero.txt

* wc -c/wc -w

	Se propone llevar las cuentas con un baco que almacenamos en la gaveta.
	Cada vez que leamos una lnea, convertimos todos sus caracteres a cuentas
	de baco, aadimos una cuenta ms que indica el cambio de lnea "\n" y
	reorganizamos las cuentas del baco. Cuando completamos la lectura del
	fichero, convertimos el nmero almacenado en forma de baco en un nmero en
	base decimal.
	

		y/./o/                       #1
		s/^/o/                       #2
		G                            #3
		s/\n//                       #4
		: ordenar                    #5
		s/^\(.*\)o\{10\}\n\?/\1\no/  #6
		t ordenar                    #7
		$! {                         #8
		     h                       #9
			  d                       #10
		}                            #11
		s/$/\n/                      #12
		: abaco-numero               #13
		s/^\n\([^0-9]*\)/\10/        #14
		s/^o\{1\}\n\([^0-9]*\)/\11/  #15
		s/^o\{2\}\n\([^0-9]*\)/\12/  #16
		s/^o\{3\}\n\([^0-9]*\)/\13/  #17
		s/^o\{4\}\n\([^0-9]*\)/\14/  #18
		s/^o\{5\}\n\([^0-9]*\)/\15/  #19
		s/^o\{6\}\n\([^0-9]*\)/\16/  #20
		s/^o\{7\}\n\([^0-9]*\)/\17/  #21
		s/^o\{8\}\n\([^0-9]*\)/\18/  #22
		s/^o\{9\}\n\([^0-9]*\)/\19/  #23
		/^[0-9]/!b abaco-numero      #24

	#1 y #2 convierten en cuentas de baco ('o') los caracteres de la lnea. #3
	aade al final del escritorio el baco y #4 junta las cuentas
	correspondientes a la nueva lnea con las cuentas que representan las
	unidades en el baco. El bucle #4-#6 convierte diez cuentas de un
	determinado nivel del baco en una sola cuenta en el nivel superior. Si no
	se ha llegado a la ltima lnea, se guarda el baco en la gaveta y se
	comienza un nuevo ciclo sin imprimir. Por contra, s ya no quedan ms
	lneas por leer, #12-#24 convierten el nmero en formato baco en un
	nmero en base decimal, que se imprimir al acabar el ciclo.

	Obsrvese que este mismo algoritmo nos sirve para emular wc -w. Basta con
	sustituir #1 y #2 por una orden que convierta en cuenta cada palabra:

		s/[^ \t]\+/o/g
		s/[^o]*//g
	
	E, incluso, para contar la presencia de cualquier patrn:

		s/patrn/\n/g
		s/[^\n]//g
		y/\
		/o/

* cat -n (listar un fichero anteponiendo a cada lnea su nmero). cat -b

	Es necesario conservar y presentar el texto de la lnea de modo que no se
	puede usar '=', porque esta orden manda el nmero de lnea al flujo de
	salida, no al escritorio. Por ello, es imposible hacer ninguna manipulacin
	para lograr tener en una misma lnea el nmero y el texto.

	Casi todo este algoritmo se basa en lo realizado para resolver aquel que
	propona sumar uno a los nmeros que contenan las lneas de un determinado
	fichero.

		x                            #1
		s/^9*$/0&/                   #2
		s/.9*$/x&/                   #3
		G                            #4
		h                            #5
		s/[0-9]*x\([0-9]*\).*$/\1/   #6
		y/0123456789/1234567890/     #7
		G                            #8
		s/\n.*\n/ /                  #9
		x                            #10
		s/x.*//                      #11
		G                            #12
		s/\n//                       #13
		h                            #14
		s/ .*$//                     #15
		x                            #16
		

	#1 trae el nmero de lnea a la gaveta. #2 aade un 0 a los nmeros
	constituidos exclusivamente por nueves. Obsrvese que cuando la gaveta est
	vaca, hay un conjunto de 0 nueves y, por tanto, se incluye un 0; de modo
	que no es necesario tratar de forma particular este caso. #3 separa la
	parte variante de la invariante. A continuacin, #4 aade la lnea leda al
	final del escritorio y #5 copia todo en la gaveta. #6 deja en el escritorio
	slo la parte variante. #7 realiza la transformacin de esta parte, #8 y #9
	nos permiten incluir la lnea leda despus de la parte variante (la
	separacin que se ha usado es un espacio). Como an nos falta la parte
	invariante, #10 intercambia los contenidos de gaveta y escritorio.  #11
	deja slo la parte invariante y #12 y #13 pegan detrs la parte variante y
	la lnea. Ya est hecho todo.  Sin embargo, hemos de guardar el nmero de
	lnea en la gaveta para el siguiente ciclo. Por ello, #14 copia el
	escritorio en la gaveta, #15 elimina el texto de la lnea y, por ltimo,
	#16 intercambia los contenidos de la gaveta (nmero+texto) y el escritorio
	(nmero).

	Si lo que se quiere emular es cat -b, es decir, numerar las lneas excepto
	las vacas, basta con incluir al principio del guin la orden:

		/^$/b
	
	que evitara la ejecucin del algoritmo, si la lnea leda est vaca.

* unix2dos (eliminar el retorno de carro)

		sed 's/^M$//' fichero-en-unix.txt > fichero-en-dos.txt

	^M Se consigue pulsando Ctrl+V Ctrl+M.

* dos2unix (aadir retorno de carro al final de la lnea)

		sed 's/$/^M/' fichero-en-dos.txt > fichero-en-unix.txt

	^M Se consigue pulsando Ctrl+V Ctrl+M.
