¿Cómo usar la opción '-prune' de 'find' en sh?

219

No entiendo bien el ejemplo dado por el man find, ¿alguien puede darme algunos ejemplos y explicaciones? ¿Puedo combinar expresiones regulares en él?


La pregunta más detallada es así:

Escriba un script de shell changeall, que tiene una interfaz como changeall [-r|-R] "string1" "string2". Se encontrará todos los archivos con un sufijo .h, .C, .cc, o .cppy cambiar todas las apariciones de string1a string2. -res una opción para permanecer solo en el directorio actual o incluir subdirectorios.

NOTA:

  1. Para el caso no recursivo, lsNO está permitido, solo podríamos usar findy sed.
  2. Lo intenté find -depthpero NO fue compatible. Es por eso que me preguntaba si -prunepodría ayudar, pero no entendí el ejemplo man find.

EDIT2: Estaba haciendo una tarea, no hice preguntas con gran detalle porque me gustaría terminarlo yo mismo. Como ya lo hice y lo entregué, ahora puedo formular toda la pregunta. Además, logré terminar la tarea sin usar -prune, pero me gustaría aprenderla de todos modos.

derrdji
fuente

Respuestas:

438

Lo que me pareció confuso -prunees que es una acción (como -print), no una prueba (como -name). Altera la lista de "tareas", pero siempre devuelve verdadero .

El patrón general para usar -prunees este:

find [path] [conditions to prune] -prune -o \
            [your usual conditions] [actions to perform]

Casi siempre desea el -o(OR lógico) inmediatamente después -prune, porque esa primera parte de la prueba (hasta incluir -prune) devolverá falso para las cosas que realmente desea (es decir, las cosas que no desea eliminar).

Aquí hay un ejemplo:

find . -name .snapshot -prune -o -name '*.foo' -print

Esto encontrará los archivos "* .foo" que no están en los directorios ".snapshot". En este ejemplo, -name .snapshotconstituye el [conditions to prune], y -name '*.foo' -printes [your usual conditions]y [actions to perform].

Notas importantes :

  1. Si todo lo que desea hacer es imprimir los resultados, podría estar acostumbrado a omitir la -printacción. Generalmente no quieres hacer eso cuando lo usas -prune.

    El comportamiento predeterminado de find es "y" toda la expresión con la -printacción si no hay otras acciones que -prune(irónicamente) al final. Eso significa que escribir esto:

    find . -name .snapshot -prune -o -name '*.foo'              # DON'T DO THIS

    es equivalente a escribir esto:

    find . \( -name .snapshot -prune -o -name '*.foo' \) -print # DON'T DO THIS

    lo que significa que también imprimirá el nombre del directorio que está podando, que generalmente no es lo que desea. En cambio, es mejor especificar explícitamente la -printacción si eso es lo que quieres:

    find . -name .snapshot -prune -o -name '*.foo' -print       # DO THIS
  2. Si su "condición habitual" coincide con archivos que también coinciden con su condición de poda, esos archivos no se incluirán en la salida. La forma de solucionar esto es agregar un -type dpredicado a su condición de poda.

    Por ejemplo, supongamos que deseamos eliminar cualquier directorio que comenzó con .git(esto es cierto que está inventado, normalmente solo necesita eliminar la cosa nombrada exactamente .git ), pero aparte de eso desea ver todos los archivos, incluidos los archivos similares .gitignore. Puedes probar esto:

    find . -name '.git*' -prune -o -type f -print               # DON'T DO THIS

    Esto no incluiría .gitignoreen la salida. Aquí está la versión fija:

    find . -name '.git*' -type d -prune -o -type f -print       # DO THIS

findConsejo adicional: si está utilizando la versión GNU de , la página texinfo findtiene una explicación más detallada que su página de manual (como es cierto para la mayoría de las utilidades GNU).

Laurence Gonsalves
fuente
66
no es 100% obvio en su texto (pero como solo imprime '* .foo' no entra en conflicto) pero la parte -prune tampoco imprimirá nada (no solo directorios) llamado ".snapshot". es decir, -pruneno solo funciona en los directorios (sino que, para los directorios, también impide ingresar los directorios que coinciden con esa condición, es decir, aquí los directorios coinciden con eso -name .snapshot).
Olivier Dulac
12
y +1 para usted por la explicación bien hecha (y especialmente la nota importante). Debería enviar esto a los desarrolladores de búsqueda (ya que la página del manual no explica "podar" para los seres humanos normales ^^ Me tomó muchos intentos descubrirlo, y no vi ese efecto secundario del que nos advierte)
Olivier Dulac
2
@OlivierDulac Ese es un muy buen punto acerca de la eliminación potencial de archivos que desea conservar. He actualizado la respuesta para aclarar esto. Por -prunecierto, no es en sí mismo lo que causa esto. El problema es que el operador o "cortocircuita", y o tiene menor prioridad que y. El resultado final es que si .snapshotse encuentra un archivo llamado , coincidirá con el primero -name, -pruneluego no hará nada (pero devuelve verdadero), y luego el o devuelve verdadero ya que su argumento izquierdo era verdadero. La acción (por ejemplo:) -printes parte de su segundo argumento, por lo que nunca tiene la oportunidad de ejecutarse.
Laurence Gonsalves
3
+1 finalmente descubrió por qué lo necesito -printal final, ahora puedo dejar de agregar \! -path <pattern>además de-prune
Variable miserable
66
Tenga en cuenta que "-o" es la abreviatura de "-or", que (aunque no es compatible con POSIX) se lee más claramente.
yoyo
27

Normalmente, la forma nativa en que hacemos las cosas en Linux y la forma en que pensamos es de izquierda a derecha.
Entonces, primero irías y escribirías lo que estás buscando:

find / -name "*.php"

Entonces probablemente presionas enter y te das cuenta de que estás obteniendo demasiados archivos de directorios que no deseas. Excluyamos / medios para evitar buscar en sus unidades montadas.
Ahora debería APROBAR lo siguiente al comando anterior:

-print -o -path '/media' -prune

entonces el comando final es:

find / -name "*.php" -print -o -path '/media' -prune

............... | <--- Incluir ---> | .................... | <- -------- Excluir ---------> |

Creo que esta estructura es mucho más fácil y se correlaciona con el enfoque correcto.

AmitP
fuente
3
No hubiera esperado que esto fuera eficiente; hubiera pensado que evaluaría la cláusula izquierda primero antes de la ciruela, pero para mi sorpresa, una prueba rápida parece sugerir que findes lo suficientemente inteligente como para procesar la -prunecláusula primero. Hmmm, interesante.
artfulrobot
¡Nunca había considerado que en casi una década de usar GNU find! ¡Gracias por eso! Definitivamente va a cambiar la forma en que pienso a -prunepartir de ahora.
Felipe Alvarez
3
@artfulrobot ¿Realmente lo está procesando primero? Pensé que estaba entrando /media, notando que no se llama *.phpy luego comprobando si está actualmente dentro /media, viendo que sí y, por lo tanto, omitiendo todo el subárbol. Todavía es de izquierda a derecha, simplemente no hace ninguna diferencia siempre que ambas verificaciones no se superpongan.
phk
26

Tenga en cuenta que -prune no impide descender a ningún directorio como algunos han dicho. Impide descender a directorios que coinciden con la prueba a la que se aplica. Quizás algunos ejemplos ayuden (vea la parte inferior para ver un ejemplo de expresiones regulares). Perdón por que esto sea tan largo.

$ find . -printf "%y %p\n"    # print the file type the first time FYI
d .
f ./test
d ./dir1
d ./dir1/test
f ./dir1/test/file
f ./dir1/test/test
d ./dir1/scripts
f ./dir1/scripts/myscript.pl
f ./dir1/scripts/myscript.sh
f ./dir1/scripts/myscript.py
d ./dir2
d ./dir2/test
f ./dir2/test/file
f ./dir2/test/myscript.pl
f ./dir2/test/myscript.sh

$ find . -name test
./test
./dir1/test
./dir1/test/test
./dir2/test

$ find . -prune
.

$ find . -name test -prune
./test
./dir1/test
./dir2/test

$ find . -name test -prune -o -print
.
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2

$ find . -regex ".*/my.*p.$"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test/myscript.pl

$ find . -name test -prune -regex ".*/my.*p.$"
(no results)

$ find . -name test -prune -o -regex ".*/my.*p.$"
./test
./dir1/test
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test

$ find . -regex ".*/my.*p.$" -a -not -regex ".*test.*"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py

$ find . -not -regex ".*test.*"                   .
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2
Pausado hasta nuevo aviso.
fuente
si también "toca ./dir1/scripts/test" (es decir, tiene un archivo de "prueba" y no dir, en ese subdirectorio impreso), no se imprimirá con find . -name test -prune -o -print: iow, -prunees una acción que también funciona en archivos
Olivier Dulac
10

Agregando al consejo dado en otras respuestas (no tengo representante para crear respuestas) ...

Cuando se combina -prunecon otras expresiones, hay una sutil diferencia en el comportamiento dependiendo de qué otras expresiones se usen.

El ejemplo de @Laurence Gonsalves encontrará los archivos "* .foo" que no están en los directorios ".snapshot": -

find . -name .snapshot -prune -o -name '*.foo' -print

Sin embargo, esta abreviatura ligeramente diferente, quizás inadvertidamente, también .snapshotlistará el directorio (y cualquier directorio anidado .snapshot):

find . -name .snapshot -prune -o -name '*.foo'

El motivo es (según la página de manual de mi sistema): -

Si la expresión dada no contiene ninguno de los primarios -exec, -ls, -ok o -print, la expresión dada se reemplaza efectivamente por:

(given_expression) -print

Es decir, el segundo ejemplo es el equivalente a ingresar lo siguiente, modificando así la agrupación de términos:

find . \( -name .snapshot -prune -o -name '*.foo' \) -print

Esto al menos se ha visto en Solaris 5.10. Después de haber usado varios sabores de * nix durante aproximadamente 10 años, solo recientemente he buscado una razón por la cual esto ocurre.

crw
fuente
¡Gracias por llamar la atención sobre la diferencia entre usar -prunecon y sin -print!
mcw
3

La poda es un no recurse en cualquier cambio de directorio.

Desde la página del manual

Si no se da profundidad, es cierto; Si el archivo es un directorio, no descienda a él. Si se da profundidad, falso; sin efecto.

Básicamente, no desembocará en ningún subdirectorio.

Toma este ejemplo:

Tienes los siguientes directorios

  • / inicio / test2
  • / inicio / test2 / test2

Si corres find -name test2:

Devolverá ambos directorios

Si corres find -name test2 -prune:

Solo regresará / home / test2 ya que no descenderá a / home / test2 para encontrar / home / test2 / test2

AdamW
fuente
no es 100% verdadero: es "hacer poda cuando coincida la condición, y si es un directorio, retírelo de la lista de tareas, es decir, no lo ingrese también". -prune también funciona en archivos.
Olivier Dulac
2

No soy un experto en esto (y esta página fue muy útil junto con http://mywiki.wooledge.org/UsingFind )

Recién notado -pathes para una ruta que coincide completamente con la cadena / ruta que viene justo despuésfind ( .en estos ejemplos) donde -namecoincide con todos los nombres de base.

find . -path ./.git  -prune -o -name file  -print

bloquea el directorio .git en su directorio actual ( como lo encuentra en . )

find . -name .git  -prune -o -name file  -print

bloquea todos los subdirectorios .git de forma recursiva.

Tenga en cuenta que ./ es extremadamente importante !! -pathdebe coincidir con un camino anclado . o lo que sea que venga justo después de encontrar si obtiene coincidencias sin él (desde el otro lado de la o ' -o') ¡probablemente no se poden! Yo ignoraba esto ingenuamente y me puso a usar -path cuando es genial cuando no quieres podar todos los subdirectorios con el mismo nombre base: D

sabgenton
fuente
Tenga en cuenta que si lo hace find bla/, necesitaría -path bla/.git (o si empujó un a *en el frente, se comportaría más como -name)
sabgenton
1

Mostrar todo, incluido el directorio en sí, pero no sus contenidos aburridos:

find . -print -name dir -prune
Devon
fuente
0

Si lees todas las buenas respuestas aquí, ahora entiendo que lo siguiente devuelve los mismos resultados:

find . -path ./dir1\*  -prune -o -print

find . -path ./dir1  -prune -o -print

find . -path ./dir1\*  -o -print
#look no prune at all!

Pero el último tomará mucho más tiempo ya que aún busca todo en dir1. Supongo que la verdadera pregunta es cómo -orsacar resultados no deseados sin buscarlos realmente.

Así que supongo que podar significa no hacer partidos pasados ​​decentes, sino marcarlo como hecho ...

http://www.gnu.org/software/findutils/manual/html_mono/find.html "Sin embargo, esto no se debe al efecto de la acción '-prune' (que solo evita un mayor descenso, no asegura ignoramos ese elemento). En cambio, este efecto se debe al uso de '-o'. Dado que el lado izquierdo de la condición "o" ha tenido éxito para ./src/emacs, no es necesario evaluar el derecho- lado de la mano ('-print') para este archivo en particular ".

sabgenton
fuente
0

findconstruye una lista de archivos. Aplica el predicado que proporcionó a cada uno y devuelve los que pasan.

Esta idea que -prunesignifica excluir de los resultados fue realmente confusa para mí. Puede excluir un archivo sin podar:

find -name 'bad_guy' -o -name 'good_guy' -print  // good_guy

Todo lo que -prunehace es alterar el comportamiento de la búsqueda. Si la coincidencia actual es un directorio, dice "oye find, ese archivo que acabas de emparejar, no desciendas en él" . Simplemente elimina ese árbol (pero no el archivo en sí) de la lista de archivos para buscar.

Debería ser nombrado -dont-descend.

buscador de tocino
fuente
0

Hay bastantes respuestas; algunos de ellos son demasiado teóricos. Dejaré por qué necesitaba podar una vez, así que tal vez el tipo de explicación de necesidad primero / ejemplo sea ​​útil para alguien :)

Problema

Tenía una carpeta con aproximadamente 20 directorios de nodos, cada uno con su node_modulesdirectorio como se esperaba.

Una vez que entras en cualquier proyecto, ves cada uno ../node_modules/module. Pero ya sabes cómo es. Casi todos los módulos tienen dependencias, por lo que lo que estás viendo es más comoprojectN/node_modules/moduleX/node_modules/moduleZ...

No quería ahogarme con una lista con la dependencia de la dependencia de ...

Conocer -d n/ -depth n, no me habría ayudado, ya que el directorio main / first node_modules que quería de cada proyecto tenía una profundidad diferente, como esta:

Projects/MysuperProjectName/project/node_modules/...
Projects/Whatshisname/version3/project/node_modules/...
Projects/project/node_modules/...
Projects/MysuperProjectName/testProject/november2015Copy/project/node_modules/...
[...]

¿Cómo puedo obtener una lista de rutas que terminen en la primera node_modulesy pasar al siguiente proyecto para obtener la misma?

Entrar -prune

Cuando agregue -prune, seguirá teniendo una búsqueda recursiva estándar. Se analiza cada "camino", y cada hallazgo se escupe y findsigue cavando como un buen muchacho. Pero es la excavación de más de node_moduleslo que no quería.

Entonces, la diferencia es que en cualquiera de esos caminos diferentes, -prunedejará findde cavar más abajo en esa avenida en particular cuando haya encontrado su artículo. En mi caso, la node_modulescarpeta.

Carles Alcolea
fuente