Publi

Píldora: Cómo buscar un texto dentro de documentos múltiples ODT de Libreoffice/Openoffice o docx de Microsoft Word

Cuántas veces has recordado haber escrito un documento y no recuerdas dónde lo pusiste. Tienes cientos de archivos de documentos en tu disco duro, o en un servidor y no sabes por dónde empezar a buscar. Con ficheros de texto, podemos utilizar find, grep, egrep, sed y algunos comandos más que, combinados podrán darnos los resultados que buscamos. Aunque los archivos de documentos son algo más complejos internamente.

Ficheros de documentos

Tanto los ficheros de OpenOffice/LibreOffice como los documentos de Microsoft Word Open XML (esos archivos docx que se crean a partir de Microsoft Office 2007. En este post no podremos buscar documentos generados con versiones más antiguas del programa) son en realidad archivos ZIP con muchos archivos XML en su interior en los que se definen contenido, estilos y metadatos. Para los archivos odt, de LibreOffice y OpenOffice, el archivos que contiene los contenidos del documento es content.xml y en Microsoft Word, el archivo está situado en word/document.xml, así que, vamos a intentar, buscar todos los archivos de documentos que haya en una ruta del disco y examinar su contenido para buscar las palabras que necesitamos encontrar. Para ello tendremos que descomprimir el documento y extraer el archivo del documento. Todo de forma automática.

Es cierto que actualmente tenemos programas de búsqueda en escritorio, que examinan periódicamente archivos en varios formatos, procesan dicha información y agilizan la muestra de resultados. Aunque en muchos casos, para acelerar el uso normal del ordenador, muchos los solemos tener desactivados. O, por ejemplo, si tienes los contenidos en un ordenador remoto, una Raspberry PI, o un VPS, puede resultar un poco más complicado hacer dichas búsquedas.

Afortunadamente GNU/Linux nos proporciona herramientas muy buenas para realizar nuestras tareas. Primero ejecutaremos find para obtener todos los archivos de documento a partir de la ruta actual. Aunque podríamos utilizar find con otros parámetros o incluso utilizar locate. Para ello, podemos utilizar estos scripts de una línea, también llamados one-liners.

Documentos de LibreOffice/OpenOffice

Empezaremos con los documentos odt de LibreOffice u OpenOffice. Para ello extraeremos el archivo word/document.xml del archivo de documento y a partir de ahí realizaremos una búsqueda.

find -name ‘*.odt’ | while read file; do unzip -p “$file” “content.xml” | grep -li “TEXTO A BUSCAR” > /dev/null; if [ $? -eq 0 ]; then echo $file; fi; done

De esta forma, find, va pasando archivos odt al resto de la línea. Podríamos hacer fácilmente que no sean todos los archivos, si nos acordamos de parte del nombre podríamos hacer ‘*informe*.odt’ para seleccionar todos los docuentos que contentan la palabra informe y tengan extensión odt; o incluso utilizar -iname en lugar de -name para indicar que busque nombres de archivo en mayúsculas y minúsculas indistintamente. También, con find podríamos seleccionar incluso los archivos por tamaño o por fecha.

En lugar de find -name ‘*.odt’ podríamos utilizar locate ‘*.odt’ para buscar en todo el disco, o en un patrón de directorios determinado. La ventaja de locate es su gran velocidad, aunque primero tendremos que crear una base de datos de archivos del disco (con updatedb) y eso puede tardar un poco.

Una vez tenemos los nombres de archivo que cumplen el patrón, cogemos cada nombre de archivo y ejecutamos unzip -p [archivo] content.xml. La opción -p sirve para extraer el contenido de los archivos directamente a la salida estándar de la aplicación. Si ejecutamos este comando individualmente, veríamos todos los contenidos del fichero en pantalla, vamos, extrae todo a pantalla, en lugar de crear un archivo llamado content.xml. La ventaja de esto es que no necesitamos archivos temporales y todo es mucho más rápido.

La salida de unzip, es decir, el contenido del fichero comprimido, se lo pasamos a grep. En este caso, grep -li “TEXTO” > /dev/null. Con -l, hacemos que grep muestre solo los nombres de archivo donde exista coincidencia, así sólo es necesario que el texto se encuentre una vez en el archivo para que grep finalice la ejecución y devuelva un positivo. Lo malo es que si el texto no está en el archivo tendrá que analizarlo completamente. El argumento -i hace que no importen las mayúsculas y minúsculas, por lo que, aunque en el documento encontremos “CaCaHueTe”, podremos decir que grep busque “cacahuete” y lo va a encontrar sin problema. Por último, la salida de grep la redirigimos a /dev/null para que realmente no muestre nada en pantalla. ¿Por qué?

Lo que en realidad analizaremos de grep será el estado de salida del programa. Es decir, internamente, si grep acaba y el archivo que ha analizado contiene el texto que buscamos, devolverá un 0 (normalmente, cuando un programa finaliza bien devuelve un 0 y cuando no, devuelve otra cosa). Entonces, sólo si grep devuelve 0 mostraremos el nombre del archivo en pantalla. Para eso if [ $? -eq 0 ]; then echo $file; fi;.

Documentos de Microsoft Word

Para hacer lo mismo con los documentos de Microsoft Word debemos cambiar el archivo que analizamos. En este caso, analizaremos word/document.xml. Y mantendremos todo el comando igual:

find -name ‘*.docx’ | while read file; do unzip -p “$file” “word/document.xml” | grep -li “TEXTO A BUSCAR” > /dev/null; if [ $? -eq 0 ]; then echo $file; fi; done

Mejorar la búsqueda

Los ficheros que estamos analizando, en realidad son XML, por lo que, junto con el contenido encontramos algunos datos que nos indicarán estilos, objetos empotrados, notas, y demás cosas. Todo esto puede hacer que cuando busquemos algún contenido concreto no lo encontremos. Para mejorar la búsqueda, aunque penalizaremos un poco el tiempo que tardará el script, sobre todo en archivos muy grandes. La clave está en eliminar las etiquetas XML que encontramos en el archivo de contenidos. Un claro ejemplo, podemos verlo si analizamos un fichero HTML. Cuando vemos:

Hola Mundo

En realidad, el código utilizado para el texto es este:

1
<strong>Ho</strong>la Mun<strong>do</strong>

Y si realizamos una búsqueda en texto con grep, por ejemplo, buscando la palabra “Hola”. Grep no encontrará nada. Así que, pasaremos el texto por un filtro que eliminará estas etiquetas, con sed. Uno de los usos de este comando es reemplazar cadenas a través de expresiones regulares, pero aunque suene complicado, os la voy a dar preparada. Para ver un ejemplo rápido, vamos a hacer lo siguiente:

echo “Hola Mundo” | grep -i hola

Esto no devolverá nada, porque, como hemos dicho antes, no vamos a encontrar nada en la cadena anterior. Pero,
echo “Hola Mundo” | sed -e ‘s/<[^>]*>//g’ | grep -i hola
Hola Mundo

El segundo ejemplo sí que devuelve el contenido. El objetivo es sustituir globalmente (s/búsqueda/reemplazo/g) cualquier texto que esté entre < y > (<[^>]*< por un texto en blanco.

Creando el script completo

En definitiva, para buscar archivos de LibreOffice / OpenOffice, esos con extensión ODT:

find -name ‘*.odt’ | while read file; do unzip -p “$file” “content.xml” | sed -e ‘s/<[^>]*>//g’ | grep -li “TEXTO A BUSCAR” > /dev/null; if [ $? -eq 0 ]; then echo $file; fi; done

Y para buscar archivos Office Open XML de Microsoft Word, esos con extensión DOCX:

find -name ‘*.docx’ | while read file; do unzip -p “$file” “word/document.xml” | sed -e ‘s/<[^>]*>//g’ | grep -li “TEXTO A BUSCAR” > /dev/null; if [ $? -eq 0 ]; then echo $file; fi; done

Foto principal: unsplash-logoDmitry Ratushny

También podría interesarte....

There are 7 comments left Ir a comentario

  1. nascii /
    Usando Mozilla Firefox Mozilla Firefox 59.0 en Fedora Linux Fedora Linux

    muy interesante, justo ayer por culpa de un libro que ponia el codigo fuente de los ejercicios en .doc me vi en la necesidad de “buscar soluciones” para pasalos directamente a texto sin abrir monstruosos procesadores de texto

    una de las solucienos fue catdoc (de .doc a txt) y otra fue utilizar libreoffice

    libreoffice –headless –convert-to “txt:Text (encoded):UTF8” *.doc

    que puede convertir a otras cosas, directamente desde CLI

    (pd: creo que en los ultimos ejemplos de terminal, el wordpress (supongo) se esta comiendo unos caracteres de la expresion)

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 59.0 en Ubuntu Linux Ubuntu Linux

      Gracias por tu comentario!!
      libreoffice -headless también está muy bien para convertir a texto, incluso soporta el argumento –cat [documento.doc] que te permite mostrar el texto directamente en el terminal, y ya puedes analizarlo sin fichero temporal ni nada. Además, el headless mode tiene muchas opciones, puede quedarse en modo escucha y le puedes “pedir cosas” por socket. 🙂

      Estudiaré lo de los ejemplos del terminal. Ahora mismo lo veo bien, pero puede ser cosa del navegador o de la carga del script… lo mismo hace falta una nueva actualización del plugin 🙂

      Un abrazo!

  2. Oscar /
    Usando Mozilla Firefox Mozilla Firefox 59.0 en Fedora Linux Fedora Linux

    Hola.
    No entiendo el significado de la expresión regular “]*>” . En principio, lo que busca esto es el caracter “]” repetido 0 o más veces hasta llegar a un “>”. ¿Qué sentido tiene esto para encontrar cosas del tipo “”. ¿No sería más conveniente, por ejemplo, algo así como “” o más elaborado?
    Gracias

    1. Oscar /
      Usando Mozilla Firefox Mozilla Firefox 59.0 en Fedora Linux Fedora Linux

      Bueno, en el comentario se me han comido los caracteres . Quería decir que si no sería mejor hacer una expresión regular similar a “”

      1. Oscar /
        Usando Mozilla Firefox Mozilla Firefox 59.0 en Fedora Linux Fedora Linux

        Y dale, se me ha comido otra vez. Bueno, desisto

        1. Gaspar Fernández / Post Author
          Usando Mozilla Firefox Mozilla Firefox 59.0 en Ubuntu Linux Ubuntu Linux

          Hola Oscar. La expresión regular de hecho no ha salido bien. Parece que WordPress se ha comido un trozo. Llevaba razón @nascii en el comentario anterior. Voy a arreglarlo ahora.

        2. Gaspar Fernández / Post Author
          Usando Mozilla Firefox Mozilla Firefox 59.0 en Ubuntu Linux Ubuntu Linux

          La expresión es ‘s/<[^>]*>//g el objetivo es que encuentre un < y luego recorra todos los caracteres mientras no sean > y luego encuentre otro > Así puede encerrar la etiqueta XML y nos la podemos cargar a gusto.

          Un saludo!

Leave a Reply