Pregunta Cómo excluir un directorio en find. mando


Estoy tratando de ejecutar un comando de búsqueda para todos los archivos de JavaScript, pero ¿cómo excluyo un directorio específico?

Aquí está el código de búsqueda que estamos usando.

for file in $(find . -name '*.js'); do java -jar config/yuicompressor-2.4.2.jar --type js $file -o $file; done

903
2017-11-17 22:57


origen


Respuestas:


Use el interruptor de ciruela, por ejemplo, si desea excluir el misc directorio solo agrega un -path ./misc -prune -o a su comando de búsqueda:

find . -path ./misc -prune -o -name '*.txt' -print

Aquí hay un ejemplo con múltiples directorios:

find . -type d \( -path dir1 -o -path dir2 -o -path dir3 \) -prune -o -print

Aquí excluimos dir1, dir2 y dir3, ya que en find expresiones es una acción, que actúa sobre los criterios -path dir1 -o -path dir2 -o -path dir3 (si dir1 o dir2 o dir3), ANDed con type -d. Acción adicional es -o printsolo imprime


701
2017-11-17 23:00



Si -prune no funciona para usted, esto:

find -name "*.js" -not -path "./directory/*"

1518
2018-04-01 01:26



Encuentro que es más fácil razonar acerca de otras soluciones propuestas:

find build -not \( -path build/external -prune \) -name \*.js

Esto proviene de un caso de uso real, donde necesitaba llamar a yui-compressor en algunos archivos generados por wintersmith, pero omito otros archivos que deben enviarse tal como están.

Dentro \( y \) es una expresión que coincidirá exactamente  build/external (va a no partido si lo hiciste find ./build, por ejemplo, necesitas cambiarlo a ./build/external en ese caso), y, en caso de éxito, evitar atravesar cualquier cosa debajo. Esto se agrupa luego como una sola expresión con el paréntesis escapado, y con el prefijo -not que hará find omita todo lo que coincida con esa expresión.

Uno podría preguntar si agregar -not no hará que todos los demás archivos estén ocultos -prune reaparecer, y la respuesta es no. La manera -prune funciona es cualquier cosa que, una vez que se alcanza, los archivos debajo de ese directorio se ignoran permanentemente.

Eso también es fácil de ampliar para agregar exclusiones adicionales. Por ejemplo:

find build -not \( -path build/external -prune \) -not \( -path build/blog -prune \) -name \*.js

341
2018-05-16 19:01



Existe una confusión aquí en cuanto a cuál debería ser la sintaxis preferida para omitir un directorio.

Opinión de GNU

To ignore a directory and the files under it, use -prune

De la página de manual de GNU find

Razonamiento

-prune paradas find de descender a un directorio. Solo especificando -not -path seguirá descendiendo en el salteado directorio, pero -not -path será falso cada vez que find prueba cada archivo.

Problemas con -prune

-prune hace lo que se pretende, pero aún hay algunas cosas de las que debes ocuparte cuando lo usas.

  1. find imprime el directorio podado

    • CIERTO Ese es el comportamiento previsto, simplemente no desciende en él. Para evitar imprimir el directorio por completo, use una sintaxis que lógicamente lo omita.
  2. -prune solo funciona con -print y ninguna otra acción.

    • NO ES VERDAD. -prune funciona con cualquier acción excepto -delete. ¿Por qué no funciona con eliminar? por -delete para trabajar, encuentre la necesidad de recorrer el directorio en orden DFS, ya que -deleteprimero borrará las hojas, luego los padres de las hojas, etc. ... Pero para especificar -prune tener sentido, find necesita golpear un directorio y dejar de descender, lo que claramente no tiene sentido con -depth o -delete en.

Actuación

Configuré una prueba simple de las tres respuestas ascendentes arriba en esta pregunta (reemplazado -printcon -exec bash -c 'echo $0' {} \; para mostrar otro ejemplo de acción). Los resultados están abajo

----------------------------------------------
# of files/dirs in level one directories
.performance_test/prune_me     702702    
.performance_test/other        2         
----------------------------------------------

> find ".performance_test" -path ".performance_test/prune_me" -prune -o -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 23513814

> find ".performance_test" -not \( -path ".performance_test/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 10670141

> find ".performance_test" -not -path ".performance_test/prune_me*" -exec bash -c 'echo "$0"' {} \;
.performance_test
.performance_test/other
.performance_test/other/foo
  [# of files] 3 [Runtime(ns)] 864843145

Conclusión

Ambos sintaxis de f10bit y Sintaxis de Daniel C. Sobral tomó de 10 a 25 ms para ejecutarse en promedio. La sintaxis de GetFreeque no usa -prune, tomó 865ms. Entonces, sí, este es un ejemplo bastante extremo, pero si te preocupa el tiempo de ejecución y estás haciendo algo remotamente intensivo, debes usar -prune.

Nota Sintaxis de Daniel C. Sobral realizado el mejor de los dos -prune sintaxis; pero, sospecho fuertemente que esto es el resultado de un almacenamiento en caché, ya que al cambiar el orden en el que se ejecutaron los dos resultados, se obtuvo el resultado opuesto, mientras que la versión que no es de ciruela siempre fue más lenta.

Script de prueba

#!/bin/bash

dir='.performance_test'

setup() {
  mkdir "$dir" || exit 1
  mkdir -p "$dir/prune_me/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/w/x/y/z" \
    "$dir/other"

  find "$dir/prune_me" -depth -type d -exec mkdir '{}'/{A..Z} \;
  find "$dir/prune_me" -type d -exec touch '{}'/{1..1000} \;
  touch "$dir/other/foo"
}

cleanup() {
  rm -rf "$dir"
}

stats() {
  for file in "$dir"/*; do
    if [[ -d "$file" ]]; then
      count=$(find "$file" | wc -l)
      printf "%-30s %-10s\n" "$file" "$count"
    fi
  done
}

name1() {
  find "$dir" -path "$dir/prune_me" -prune -o -exec bash -c 'echo "$0"'  {} \;
}

name2() {
  find "$dir" -not \( -path "$dir/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
}

name3() {
  find "$dir" -not -path "$dir/prune_me*" -exec bash -c 'echo "$0"' {} \;
}

printf "Setting up test files...\n\n"
setup
echo "----------------------------------------------"
echo "# of files/dirs in level one directories"
stats | sort -k 2 -n -r
echo "----------------------------------------------"

printf "\nRunning performance test...\n\n"

echo \> find \""$dir"\" -path \""$dir/prune_me"\" -prune -o -exec bash -c \'echo \"\$0\"\'  {} \\\;
name1
s=$(date +%s%N)
name1_num=$(name1 | wc -l)
e=$(date +%s%N)
name1_perf=$((e-s))
printf "  [# of files] $name1_num [Runtime(ns)] $name1_perf\n\n"

echo \> find \""$dir"\" -not \\\( -path \""$dir/prune_me"\" -prune \\\) -exec bash -c \'echo \"\$0\"\' {} \\\;
name2
s=$(date +%s%N)
name2_num=$(name2 | wc -l)
e=$(date +%s%N)
name2_perf=$((e-s))
printf "  [# of files] $name2_num [Runtime(ns)] $name2_perf\n\n"

echo \> find \""$dir"\" -not -path \""$dir/prune_me*"\" -exec bash -c \'echo \"\$0\"\' {} \\\;
name3
s=$(date +%s%N)
name3_num=$(name3 | wc -l)
e=$(date +%s%N)
name3_perf=$((e-s))
printf "  [# of files] $name3_num [Runtime(ns)] $name3_perf\n\n"

echo "Cleaning up test files..."
cleanup

153
2017-07-04 00:21



Una opción sería excluir todos los resultados que contengan el nombre del directorio con grep. Por ejemplo:

find . -name '*.js' | grep -v excludeddir

44
2017-11-17 23:01



Prefiero el -not notación ... es más legible:

find . -name '*.js' -and -not -path directory

37
2017-11-17 23:22



Use la opción -prune. Entonces, algo como:

find . -type d -name proc -prune -o -name '*.js'

El '-type d -name proc -prune' solo busca directorios llamados proc para excluir.
El '-o' es un operador 'OR'.


17
2017-11-17 23:01