Uso avanzado de Find y Grep

comando grep

Durante todos mis años de desarrollado LAMP, han habido una serie de comandos UNIX que me han sido muy útiles. En este artículo voy a compartir unos ejemplos de su uso que van más allá de su empleo básico, con el objetivo de que sirva de ayuda para otros programadores, así como de recordatorio para mi pobre memoria. Si eres administrador de sistemas, probablemente no te parecerá especialmente avanzado lo que aquí se explica, pero por favor recuerda quién es el público objetivo.

En esta ocasión voy a centrarme en find y grep, más adelante me gustaría ampliar este artículo con otros comandos que facilitan la vida al programador y al usuario de Unix / Linux en general.

Entrada, salida y su redireccionamiento

Antes de empezar, no estaría de más, para una mayor claridad de los ejemplos, explicar que en Unix el standard output (salida) e input (entrada) es la consola. Debemos tenerlo en cuenta para redireccionar tanto la salida como la entrada de datos de las órdenes mediante pipelines ( < > | )

find y grep

Grep es una poderosa herramienta que nos permite encontrar coincidencias dentro de un fichero o de un conjunto de ellos, gran parte de sus «superpoderes» emanan de las expresiones regulares, las que se activan mediante la opción «-E». Ahora bien, los caracteres especiales para expresiones regulares como |, (, ), { y } pueden activarse simplemente anteponiendo la barra invertida \. Ciertamente, esto es muy contraintuitivo, pues \ siempre se emplea para el escape. A continuación, veamos unos ejemplos de búsqueda en el fichero borrame.txt:

grep "regex|regex not" borrame.txt

Esto busca la cadena exacta «regex|regex not» dentro del archivo. Para que la barra vertical | actúe como en una expresión regular, se puede activar:

grep "regex\|regex not" borrame.txt

O simplemente se puede indicar que se trata de una expresión regular:


        grep -E "regex|regex not" borrame.txt
    

Lo cual es equivalente a emplear egrep, que aunque pueda parecerlo no se trata de un comando independiente sino de un mero envoltorio para grep:


        egrep "regex|regex not" borrame.txt
    
  • Para buscar una cadena de texto en ficheros filtrados recursivamente:
grep -r --include="*.php" "yorkshiresConfig.ini" . > resultados.txt

Esta orden buscaría recursivamente, a través de todos los subdirectorios, la cadena «yorkshiresConfig.ini» en todos los ficheros con extensión php y volcaría las coincidencias en el fichero resultados.txt.

  • El nombre de la orden find ya nos dice todo acerca de su función. Por ejemplo, para buscar recursivamente todos los directorios que empiecen por «land»:
find . -name "land*" -type d

Se vuelve realmente poderosa cuando se combina con otras órdenes mediante su parámetro «-exec». He aquí algunos ejemplos:

  • Para borrar ficheros resultantes de una búsqueda de cierto tipo de ficheros, con los subdirectorios incluidos:
find -name '*.po' -exec rm -i {} \;

La barra invertida y punto y coma final \; indican a find dónde terminan los parámetros del comando rm. Esta especie de muro de contención aparecerá con frecuencia en los siguientes ejemplos.

  • Para borrar todos los ficheros que tengan más de 50 días:
find /compartido/backups/pro/auto/bd/ -mtime +50 -exec rm {} \;

Como fácilmente podemos acabar borrando lo que no deseábamos, con la opción -i el programa rm pedirá confirmación por cada fichero.

  • En vez usar la opción «exec» para ejecutar el comando rm, find también dispone de la acción «delete». Este es un ejemplo que borra todo el contenido del directorio .cache al que hace más de un año que no se accede:
find ~/.cache/ -type f -atime +356 -delete
  • Para moverlos a otro directorio:
find -name '*.po' -exec mv {} ./poBackups \;
  • También podemos renombrar masivamente ficheros, el siguiente comando busca recursivamente todos los ficheros «.po» y los renombra a «.po.bak»:
find -name '*.po' | sed 's/\(.*\).po$/mv "&" "\1.po.bak"/' | sh

Aquí sale a brillar el viejo comando «sed». Al igual que se vio con grep, permite la concesión de superpoderes a ciertos caracteres con la barra invertida, en este caso a los paréntesis. Por un lado de la pipeline recoge el nombre (cadena de texto) de cada fichero .po que encuentra find, le cambia la parte final del nombre a .po.bak, le añade la cadena «mv » al principio y por el otro lado de la pipeline se lo pasa a sh, el intérprete de la shell. Ejemplo: find encuentra el fichero «es.po», sed crea la cadena ‘mv «es.po» «es.po.bak»‘ y la shell ejecuta la cadena.

Si en vez de «find -name ‘*.po»‘ usáramos «ls -d *.po» serían sólo los directorios del directorio actual.

  • Busca en el directorio actual y en los subdirectorios todos los ficheros con la extensión php que contengan la cadena «Possible reason: your host may have disabled the mail«:
find -name '*.php' -exec grep -H 'Possible reason: your host may have disabled the mail' {} \;
  • Como ya hemos visto, si el listado de resultados es demasiado grande como para manejarlo cómodamente en el terminal, podemos enviarlo mediante una pipe desde el standard output a un fichero:
find -name '*.php' -exec grep -H 'Possible reason: your host may have disabled the mail' {} \; > results.txt
  • Si de cada fichero encontrado queremos saber su ruta exacta, debemos añadir a find «-print» al final:
find -name '*.php' -exec grep -q 'Possible reason: your host may have disabled the mail' {} \; -print

A grep se le añade la opción «-q» (quiet, silencioso) para que la salida de la orden no sea confusa.

Además, find también nos permite buscar por fechas:

  • Encontrar ficheros modificados (porque su contenido haya cambiado, no únicamente sus metadatos) en el último día:
find -mtime -1 -type f
  • Si en vez de «f» usamos «d«, le indicamos que trabaje sólo con directorios:
find -ctime -1 -type d
  • Encontrar ficheros más recientes que main.css:
find -newer main.css
  • Contar el número de ficheros de un directorio y subdirectorios:
find . -type f | wc -l

ls y mv al rescate

A veces, las herramientas más básicas son las adecuadas. Esto es especialmente cierto en UNIX, pues en su tradición está la filosofía de disponer de muchas pequeñas herramientas para tareas muy concretas, permitiendo al usuario combinarlas para conseguir objetivos más complejos.

  • Para contar los elementos del directorio actual podemos usar la orden wc (Word Count), en combinación con ls. El parámetro -l indica que cuente líneas en vez de palabras, por lo tanto, contará el número de líneas que devuelve ls:
ls -1 | wc -l
  • Listar sólo los directorios, sin los ficheros, del directorio actual:
ls -d */
  • Listar sólo los directorios de un subdirectorio:
ls -d Proyectos/*/
  • Mover sólo los ficheros ocultos de un directorio (origen) a otro (dest) sin entrar en los subdirectorios:
find origen/ -maxdepth 1 -name ".*" -type f -exec mv {} dest/ \;

¡Recibe las próximas publicaciones!

¡No recibirás spam!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.