find-exec ¿una función de shell en Linux?


¿Hay una manera de obtener find para ejecutar una función que defino en el shell? Por ejemplo:

dosomething () {
  echo "doing something with $1"
}
find . -exec dosomething {} \;

El resultado de esto es:

find: dosomething: No such file or directory

¿Hay una manera de obtener find's -exec para ver dosomething?

Author: jww, 2010-12-01

12 answers

Dado que solo el shell sabe cómo ejecutar las funciones del shell, usted tiene que ejecutar un shell para ejecutar una función. También debe marcar su función para exportar con export -f, de lo contrario la subcapa no las heredará:

export -f dosomething
find . -exec bash -c 'dosomething "$0"' {} \;
 197
Author: Adam Rosenfield,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-22 05:13:58
find . | while read file; do dosomething "$file"; done
 83
Author: Jac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-13 12:39:32

Agregue comillas en {} como se muestra a continuación:

export -f dosomething
find . -exec bash -c 'dosomething "{}"' \;

Esto corrige cualquier error debido a caracteres especiales devueltos por find, por ejemplo archivos con paréntesis en su nombre.

 17
Author: Wagner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-16 02:29:20

La respuesta de Jak arriba es grande pero tiene un par de trampas que son fácilmente superadas:

find . -print0 | while IFS= read -r -d '' file; do dosomething "$file"; done

Esto usa null como delimitador en lugar de un salto de línea, por lo que los nombres de archivo con saltos de línea funcionarán. También usa la bandera -r que deshabilita el escape de barra invertida, sin ella las barras invertidas en los nombres de archivo no funcionarán. También borra IFS para que los posibles espacios en blanco finales en los nombres no se descarten.

 12
Author: pajamian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-13 21:55:22

Haga que el script se llame a sí mismo, pasando cada elemento encontrado como un argumento:

#!/bin/bash

if [ ! $1 == "" ] ; then
   echo "doing something with $1"
   exit 0
fi

find . -exec $0 {} \;

exit 0

Cuando ejecuta el script por sí mismo, encuentra lo que está buscando y se llama a sí mismo pasando cada resultado de find como argumento. Cuando el script se ejecuta con un argumento, ejecuta los comandos en el argumento y luego sale.

 8
Author: Mike Maready,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-22 18:10:38

El procesamiento de resultados a granel

Para aumentar la eficiencia, muchas personas usan xargs para procesar los resultados a granel, pero es muy peligroso. Debido a eso, se introdujo un método alternativo en find que ejecuta los resultados en masa.

Tenga en cuenta que este método podría venir con algunas advertencias como por ejemplo un requisito en POSIX-find para tener {} al final del comando.

export -f dosomething
find . -exec bash -c 'for f; do dosomething "$f"; done' _ {} +

find pasará muchos resultados como argumentos a una sola llamada de bash y el for-loop itera a través de esos argumentos, ejecutando la función dosomething en cada uno de ellos.

La solución anterior comienza los argumentos en $1, por lo que hay un _ (que representa $0).

Procesando los resultados uno por uno

De la misma manera, creo que la respuesta superior aceptada debe corregirse para ser{[19]]}

export -f dosomething
find . -exec bash -c 'dosomething "$1"' _ {} \;

Esto no solo es más sensato, porque los argumentos siempre deben comenzar en $1, sino que también usar $0 podría conducir a comportamiento inesperado si el nombre de archivo devuelto por find tiene un significado especial para el shell.

 6
Author: Dominik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-08 18:33:00

Para aquellos de ustedes que buscan una función bash que ejecutará un comando dado en todos los archivos en el directorio actual, he compilado una de las respuestas anteriores:

toall(){
    find . -type f | while read file; do "$1" "$file"; done
}

Tenga en cuenta que rompe con nombres de archivo que contienen espacios (ver más abajo).

Como ejemplo, tome esta función:

world(){
    sed -i 's_hello_world_g' "$1"
}

Digamos que quería cambiar todas las instancias de hello to world en todos los archivos del directorio actual. Yo haría:

toall world

Para estar seguro con cualquier símbolo en los nombres de archivo, use:

toall(){
    find . -type f -print0 | while IFS= read -r -d '' file; do "$1" "$file"; done
}

(pero necesita un find que maneje -print0 por ejemplo, GNU find).

 2
Author: Jason Basanese,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-09 17:54:51

No es posible ejecutar una función de esa manera.

Para superar esto, puede colocar su función en un script de shell y llamarla desde find

# dosomething.sh
dosomething () {
  echo "doing something with $1"
}
dosomething $1

Ahora úsalo en find como:

find . -exec dosomething.sh {} \;
 1
Author: codaddict,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-01 05:29:00

Coloque la función en un archivo separado y obtenga find para ejecutarla.

Las funciones del Shell son internas al shell en el que están definidas; find nunca podrán verlas.

 1
Author: Angus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-01 05:29:14

Encuentro la manera más fácil de seguir, repitiendo dos comandos en single do

func_one () {
  echo "first thing with $1"
}

func_two () {
  echo "second thing with $1"
}

find . -type f | while read file; do func_one $file; func_two $file; done
 1
Author: edib,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-07 09:12:02

No directamente, no. Find se ejecuta en un proceso separado, no en tu shell.

Crea un script de shell que haga el mismo trabajo que tu función y encuentra can -exec eso.

 0
Author: Laurence Gonsalves,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-01 05:27:57

Evitaría usar -exec por completo. ¿Por qué no usar xargs?

find . -name <script/command you're searching for> | xargs bash -c
 -4
Author: Barry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-11 22:16:21