lunes, 14 de enero de 2019

Ejecutar funciones bash dentro del comando find

El comando find es muy potente y se usa habitualmente en shell scripts. En ocasiones, se requiere ejecutar una serie de acciones por cada uno de los ficheros encontrados por find y esto puede resultar una tarea imposible. Veamos un ejemplo.
Propósito
Se requiere analizar todos los ficheros con extensión .py ubicados dentro de un directorio para detectar todos aquellos que no contienen la declaración de la codificación de texto utilizada. Para estos ficheros, añadir automáticamente la directiva siguiente:
# -*- coding: latin-1 -*-
Implementación
Vamos a realizar una primera aproximación que podría ser obvia:
#!/usr/bin/env bash

analizar_fichero () {
  n=$(grep "coding: latin-1" $1 | wc -l)
  if [ $n -eq 0 ]
  then
    sed -i "1 s/^\(.*$\)/\1\n# -*- coding: latin-1 -*-/" $1
    echo "Se modifica fichero $1"
  else
    echo "Fichero correcto. No se modifica."
  fi  
}

find . -type f -iname "*.py" -exec analizar_fichero {} \;
Se define una función bash en la que se implementa todo el análisis a un fichero específico: se comprueba si tiene la cadena buscada y si no la encuentra se la añade como segunda linea. En cualquier caso, se indica por pantalla si se realiza alguna modificación o no.
Sin embargo, al ejecutar el script se detecta que find no es capaz de entender que analizar_fichero es una función que hemos definido:
find: «analizar_fichero»: No existe el archivo o el directorio
find: «analizar_fichero»: No existe el archivo o el directorio
find: «analizar_fichero»: No existe el archivo o el directorio
find: «analizar_fichero»: No existe el archivo o el directorio
find: «analizar_fichero»: No existe el archivo o el directorio
Por tanto, se modifica el script para poder ejecutar funciones de bash para cada una de las salidas de find. El script, ya funcional, quedaría de la siguiente forma:
analizar_fichero () {
  n=$(grep "coding: latin-1" $1 | wc -l)
  if [ $n -eq 0 ]
  then
    sed -i "1 s/^\(.*$\)/\1\n# -*- coding: latin-1 -*-/" $1
    echo "Se modifica fichero $1"
  else
    echo "Fichero correcto. No se modifica."
  fi  
}

find . -type f -iname "*.py" | while read linea; do analizar_fichero $linea; done