Use el comando buscar pero excluya archivos en dos directorios

86

Quiero buscar archivos que terminen con _peaks.bed, pero excluir archivos en las carpetas tmpy scripts.

Mi comando es así:

 find . -type f \( -name "*_peaks.bed" ! -name "*tmp*" ! -name "*scripts*" \)

Pero no funcionó. Los archivos tmpy la scriptcarpeta se seguirán mostrando.

¿Alguien tiene ideas sobre esto?

Hanfei Sun
fuente

Respuestas:

189

Así es como puede especificar eso con find:

find . -type f -name "*_peaks.bed" ! -path "./tmp/*" ! -path "./scripts/*"

Explicación:

  • find . - Iniciar la búsqueda desde el directorio de trabajo actual (de forma recursiva de forma predeterminada)
  • -type f- Especifique findque solo desea archivos en los resultados
  • -name "*_peaks.bed" - Busque archivos cuyo nombre termine en _peaks.bed
  • ! -path "./tmp/*" - Excluir todos los resultados cuya ruta comience con ./tmp/
  • ! -path "./scripts/*" - También excluir todos los resultados cuya ruta comience con ./scripts/

Prueba de la solución:

$ mkdir a b c d e
$ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
$ find . -type f ! -path "./a/*" ! -path "./b/*"

./d/4
./c/3
./e/a
./e/b
./e/5

Estuviste bastante cerca, la -nameopción solo considera el nombre de base, donde as -pathconsidera la ruta completa =)

sampson-chen
fuente
Buen trabajo. Sin embargo, olvidó una de las cosas que quería el OP, encontrar archivos que terminen en _peaks.bed.
alex
2
Esto usa una serie de extensiones en GNU find, pero dado que la pregunta está etiquetada como Linux, eso no es un problema. Buena respuesta.
Jonathan Leffler
1
Una nota breve: si lo usa .en el indicador de búsqueda inicial, debe usarlo en cada ruta que excluya. La coincidencia de rutas es bastante estricta, no realiza búsquedas difusas. Entonces, si lo usas, find / -type f -name *.bed" ! -path "./tmp/"no va a funcionar. necesitas tener ! -path "/tmp"que hacerlo feliz.
peelman
3
Importante tener en cuenta que el * es importante. $ ! -path "./directory/*"
Thomas Bennett
3
Según las páginas del manual: "Para ignorar un árbol de directorios completo, utilice en -prunelugar de comprobar todos los archivos del árbol". Si sus directorios excluidos son muy profundos o tienen toneladas de archivos y le importa el rendimiento, utilice la -pruneopción en su lugar.
jueves
8

Aquí hay una forma en que puede hacerlo ...

find . -type f -name "*_peaks.bed" | egrep -v "^(./tmp/|./scripts/)"
alex
fuente
2
Esto tiene el mérito de trabajar con cualquier versión de findGNU, y no solo con GNU find. Sin embargo, la pregunta está etiquetada como Linux, por lo que no es crítica.
Jonathan Leffler
2

Utilizar

find \( -path "./tmp" -o -path "./scripts" \) -prune -o  -name "*_peaks.bed" -print

o

find \( -path "./tmp" -o -path "./scripts" \) -prune -false -o  -name "*_peaks.bed"

o

find \( -path "./tmp" -path "./scripts" \) ! -prune -o  -name "*_peaks.bed"

El orden es importante. Evalúa de izquierda a derecha. Comience siempre con la exclusión del camino.

Explicación

No utilice -not(o !) para excluir todo el directorio. Utilice -prune. Como se explica en el manual:

−prune    The primary shall always evaluate as  true;  it
          shall  cause  find  not  to descend the current
          pathname if it is a directory.  If  the  −depth
          primary  is specified, the −prune primary shall
          have no effect.

y en el manual de búsqueda de GNU:

-path pattern
              [...]
              To ignore  a  whole
              directory  tree,  use  -prune rather than checking
              every file in the tree.

De hecho, si usa -not -path "./pathname", find evaluará la expresión para cada nodo debajo "./pathname".

Las expresiones de búsqueda son solo evaluación de condiciones.

  • \( \)- Operación de grupos (puede usar -path "./tmp" -prune -o -path "./scripts" -prune -o, pero es más detallado).
  • -path "./script" -prune- si -pathdevuelve verdadero y es un directorio, devuelve verdadero para ese directorio y no descienda a él.
  • -path "./script" ! -prune- se evalúa como (-path "./script") AND (! -prune). Revierte el "siempre verdadero" de podar a siempre falso. Evita imprimir "./script"como un partido.
  • -path "./script" -prune -false- dado que -prunesiempre devuelve verdadero, puede seguirlo con -falsepara hacer lo mismo que !.
  • -o- Operador OR. Si no se especifica ningún operador entre dos expresiones, el valor predeterminado es el operador AND.

Por lo tanto, \( -path "./tmp" -o -path "./scripts" \) -prune -o -name "*_peaks.bed" -printse expande a:

[ (-path "./tmp" OR -path "./script") AND -prune ] OR ( -name "*_peaks.bed" AND print )

La impresión es importante aquí porque sin ella se expande a:

{ [ (-path "./tmp" OR -path "./script" )  AND -prune ]  OR (-name "*_peaks.bed" ) } AND print

-printse agrega mediante buscar; es por eso que la mayoría de las veces, no es necesario agregarlo en su expresión. Y dado que -prunedevuelve verdadero, imprimirá "./script" y "./tmp".

No es necesario en los demás porque cambiamos -prunepara devolver siempre falso.

Sugerencia: puede usar find -D opt expr 2>&1 1>/dev/nullpara ver cómo se optimiza y expande,
find -D search expr 2>&1 1>/dev/nullpara ver qué ruta está marcada.

f380cedric
fuente
0

Prueba algo como

find . \( -type f -name \*_peaks.bed -print \) -or \( -type d -and \( -name tmp -or -name scripts \) -and -prune \)

y no se sorprenda demasiado si me equivoco un poco. Si el objetivo es un ejecutivo (en lugar de imprimir), simplemente sustitúyalo en su lugar.

DrC
fuente
0

para mí, esta solución no funcionó en un comando ejecutivo con find, realmente no sé por qué, así que mi solución es

find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

Explicación: igual que sampson-chen uno con las adiciones de

-prune - ignora la ruta de procedimiento de ...

-o - Entonces, si no coincide, imprima los resultados (pode los directorios e imprima los resultados restantes)

18:12 $ mkdir a b c d e
18:13 $ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
18:13 $ find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

gzip: . is a directory -- ignored
gzip: ./a is a directory -- ignored
gzip: ./b is a directory -- ignored
gzip: ./c is a directory -- ignored
./c/3:    0.0% -- replaced with ./c/3.gz
gzip: ./d is a directory -- ignored
./d/4:    0.0% -- replaced with ./d/4.gz
gzip: ./e is a directory -- ignored
./e/5:    0.0% -- replaced with ./e/5.gz
./e/a:    0.0% -- replaced with ./e/a.gz
./e/b:    0.0% -- replaced with ./e/b.gz
al3x2ndru
fuente
La respuesta aceptada no funcionó, pero funciona. El uso de ciruela pasa, find . -path ./scripts -prune -name '*_peaks.bed' -type f. No estoy seguro de cómo excluir varios directorios. Esto también enumera el directorio excluido de nivel superior aunque typeesté especificado. La exclusión a través de Grep parece más sencilla a menos que desee utilizar Prune para acelerar la operación de búsqueda.
Mohnish
También tuve problemas para excluir varios directorios, pero los comentarios anteriores me dieron una respuesta que funcionó. Utilizo varias instancias de '-not -path' y en cada expresión de ruta incluyo el prefijo completo como se usa en el primer parámetro para 'buscar' y terminar cada una con un asterisco (y escapar de los puntos).
jetset
0

Puedes probar a continuación:

find ./ ! \( -path ./tmp -prune \) ! \( -path ./scripts -prune \) -type f -name '*_peaks.bed'
Jacky Jiang
fuente
2
En una pregunta anterior como esa (¡4 años!), Desea explicar por qué esta nueva respuesta es mejor o diferente, no solo el código de "volcado".
Nic3500