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
?
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"' {} \;
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
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.
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.
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.
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.
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
).
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 {} \;
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.
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
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.
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
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