linux - files - man find
Cómo excluir un directorio en encontrar. mando (30)
Estoy intentando ejecutar un comando de find
para todos los archivos JavaScript, pero ¿cómo excluyo un directorio específico?
Aquí está el código de find
que estamos usando.
for file in $(find . -name ''*.js'')
do
java -jar config/yuicompressor-2.4.2.jar --type js $file -o $file
done
Claramente, existe cierta confusión 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
Desde la página de manual de GNU find.
Razonamiento
-prune
detiene find
de descender a un directorio. Simplemente especificando -not -path
seguirá descendiendo en el directorio omitido , pero -not -path
será falsa cada vez que find
pruebas en cada archivo.
Problemas con la -prune
-prune
hace lo que se pretende, pero todavía hay algunas cosas que debes cuidar cuando lo -prune
.
find
imprime el directorio podado.- VERDADERO Ese es el comportamiento previsto, simplemente no desciende hacia él. Para evitar imprimir el directorio por completo, use una sintaxis que lógicamente lo omita.
-prune
solo funciona con-print
y no otras acciones.- No es cierto
-prune
funciona con cualquier acción excepto-delete
. ¿Por qué no funciona con eliminar? Para que-delete
funcione, encontrar necesita atravesar el directorio en el orden DFS, ya que-delete
primero eliminará las hojas, luego los padres de las hojas, etc. Pero para especificar-prune
para que tenga sentido,find
necesidades para golpear un directorio y deje de descenderlo, lo que claramente no tiene sentido con-depth
o-delete
on.
- No es cierto
Actuación
Configuré una prueba simple de las tres respuestas con los mejores votos en esta pregunta (reemplazé -print
con -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
Tanto la sintaxis de f10bit como la de Daniel C. Sobral tomaron de 10 a 25 ms en promedio. La sintaxis de GetFree , que no usa -prune
, tomó 865ms. Entonces, sí, este es un ejemplo bastante extremo, pero si te importa el tiempo de ejecución y estás haciendo algo de forma remota intensiva, debes usar -prune
.
Tenga en cuenta que la sintaxis de Daniel C. Sobral fue la mejor de las dos sintaxis de las -prune
; pero, sospecho fuertemente que esto es el resultado de cierto almacenamiento en caché, ya que cambiar el orden en el que se ejecutaron los dos resultó en el resultado opuesto, mientras que la versión sin podar siempre fue la 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
El enfoque -path -prune también funciona con comodines en la ruta. Aquí hay una declaración de búsqueda que encontrará los directorios para un servidor git que sirve múltiples repositorios de git sin incluir los directorios internos de git:
find . -type d /
-not /( -path */objects -prune /) /
-not /( -path */branches -prune /) /
-not /( -path */refs -prune /) /
-not /( -path */logs -prune /) /
-not /( -path */.git -prune /) /
-not /( -path */info -prune /) /
-not /( -path */hooks -prune /)
Encontré el nombre de las funciones en los archivos de origen de C exclude * .o y exclude * .swp y exclude (no es un archivo normal) y excluye la salida de directorio con este comando:
find . /( ! -path "./output/*" /) -a /( -type f /) -a /( ! -name ''*.o'' /) -a /( ! -name ''*.swp'' /) | xargs grep -n soc_attach
Encuentro lo siguiente más fácil de razonar que 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 omitir otros archivos que deben enviarse tal cual.
Inside /(
y /)
es una expresión que coincidirá exactamente con build/external
( no coincidirá si find ./build
, por ejemplo, debe cambiarlo a ./build/external
en ese caso), y En el éxito, evitará atravesar cualquier cosa a continuación . Luego se agrupa como una sola expresión con el paréntesis de escape, y tiene el prefijo -not
que hará que la find
omita todo lo que coincidió con esa expresión.
Uno podría preguntar si agregar -not
no hará que todos los demás archivos ocultos por -prune
reaparecer, y la respuesta es no. La forma en que funciona -prune
es que todo lo que, una vez que se alcanza, los archivos debajo de ese directorio se ignoran permanentemente.
Eso también es fácil de expandir para agregar exclusiones adicionales. Por ejemplo:
find build -not /( -path build/external -prune /) -not /( -path build/blog -prune /) -name /*.js
Estaba utilizando find
para proporcionar una lista de archivos para xgettext
, y quería omitir un directorio específico y su contenido. -path
muchas permutaciones de -path
combinadas con -prune
pero no -prune
excluir completamente el directorio que quería que se fuera.
Aunque pude ignorar el contenido del directorio que quería ignorar, find
luego devolvió el directorio como uno de los resultados, lo que provocó que xgettext
bloquee como resultado (no acepta directorios; solo archivos).
Mi solución fue simplemente usar grep -v
para omitir el directorio que no quería en los resultados:
find /project/directory -iname ''*.php'' -or -iname ''*.phtml'' | grep -iv ''/some/directory'' | xargs xgettext
Si hay o no un argumento para find
que funcionará al 100%, no puedo decirlo con certeza. Usar grep
fue una solución rápida y fácil después de un dolor de cabeza.
Este es el único que trabajó para mí.
find / -name NameOfFile ! -path ''*/Directory/*''
Buscando "NameOfFile" excluyendo "Directory". Dar énfasis a las estrellas *.
Este es el formato que utilicé para excluir algunas rutas:
$ find ./ -type f -name "pattern" ! -path "excluded path" ! -path "excluded path"
Usé esto para encontrar todos los archivos que no están en las rutas ". *":
$ find ./ -type f -name "*" ! -path "./.*" ! -path "./*/.*"
Esto es adecuado para mí en una Mac:
find . -name *.php -or -path "./vendor" -prune -or -path "./app/cache" -prune
app/cache
directorio del vendor
y de la app/cache
para el nombre de búsqueda que tiene el sufijo php
.
Esto funciona porque find
TESTS los archivos para el patrón " * foo * ":
find ! -path "dir1" ! -path "dir2" -name "*foo*"
pero NO funciona si no usa un patrón (la find
no PRUEBA el archivo). Por lo tanto, find
no hace uso de sus valores anteriores evaluados " true " y " false ". Ejemplo para no trabajar caso de uso con notación anterior:
find ! -path "dir1" ! -path "dir2" -type f
No hay ninguna prueba de find
! Por lo tanto, si necesita encontrar archivos sin ninguna coincidencia de patrones, use la tecla -prune. Además, el uso de poda es siempre más rápido, mientras que realmente salta los directorios en lugar de hacerlo o mejor no. Entonces en ese caso usa algo como:
find dir -not /( -path "dir1" -prune /) -not /( -path "dir2" -prune /) -type f
o:
find dir -not /( -path "dir1" -o -path "dir2" -prune /) -type f
Saludos
Hay muchas respuestas buenas, solo me tomó un tiempo comprender qué era cada elemento del comando y la lógica detrás de él.
find . -path ./misc -prune -o -name ''*.txt'' -print
find empezará a buscar archivos y directorios en el directorio actual, de ahí la find .
.
La opción -o
representa un OR lógico y separa las dos partes del comando:
[ -path ./misc -prune ] OR [ -name ''*.txt'' -print ]
Cualquier directorio o archivo que no sea el directorio ./misc no pasará la primera -path ./misc
prueba -path ./misc
. Pero serán probados contra la segunda expresión. Si su nombre corresponde al patrón *.txt
se imprimen, debido a la opción -print
.
Cuando find llega al directorio ./misc, este directorio solo satisface la primera expresión. Así que la opción -prune
se aplicará a ella. Le dice al comando de búsqueda que no explore ese directorio. Por lo tanto, cualquier archivo o directorio en ./misc ni siquiera será explorado por find, no se probará contra la segunda parte de la expresión y no se imprimirá.
He encontrado las sugerencias en esta página y muchas otras páginas simplemente no funcionan en mi sistema Mac OS X. Sin embargo, he encontrado una variación que funciona para mí.
La gran idea es buscar en Macintosh HD, pero evite atravesar todos los volúmenes externos, que son en su mayoría copias de seguridad de Time Machine, copias de seguridad de imágenes, recursos compartidos montados y archivos, pero sin tener que desmontarlos todos, lo que a menudo no es práctico.
Aquí está mi script de trabajo, que he llamado "findit".
#!/usr/bin/env bash
# inspired by http://.com/questions/4210042/exclude-directory-from-find-command Danile C. Sobral
# using special syntax to avoid traversing.
# However, logic is refactored because the Sobral version still traverses
# everything on my system
echo ============================
echo find - from cwd, omitting external volumes
date
echo Enter sudo password if requested
sudo find . -not /( /
-path ./Volumes/Archive -prune -o /
-path ./Volumes/Boot/ OS/ X -prune -o /
-path ./Volumes/C /
-path ./Volumes/Data -prune -o /
-path ./Volumes/jas -prune -o /
-path ./Volumes/Recovery/ HD -prune -o /
-path ./Volumes/Time/ Machine/ Backups -prune -o /
-path ./Volumes/SuperDuper/ Image -prune -o /
-path ./Volumes/userland -prune /
/) -name "$1" -print
date
echo ============================
iMac2:~ jas$
Las diversas rutas tienen que ver con volúmenes de archivo externos, Time Machine, máquinas virtuales, otros servidores montados, etc. Algunos de los nombres de volúmenes tienen espacios en ellos.
Una buena ejecución de prueba es "findit index.php", porque ese archivo se produce en muchos lugares de mi sistema. Con este script, demora unos 10 minutos buscar el disco duro principal. Sin esas exclusiones, lleva muchas horas.
Intenté el comando anterior, pero ninguno de los que usan "-prune" funciona para mí. Eventualmente probé esto con el siguiente comando:
find . /( -name "*" /) -prune -a ! -name "directory"
Ninguna de las respuestas anteriores es buena en Ubuntu. Prueba esto:
find . ! -path "*/test/*" -type f -name "*.js" ! -name "*-min-*" ! -name "*console*"
He encontrado esto here
Para aquellos de ustedes en versiones anteriores de UNIX que no pueden usar -path o -not
Probado en SunOS 5.10 bash 3.2 y SunOS 5.11 bash 4.4
find . -type f -name "*" -o -type d -name "*excluded_directory*" -prune -type f
Para excluir directorios múltiples:
find . -name ''*.js'' -not /( -path "./dir1" -o -path "./dir2/*" /)
Para agregar directorios, agregue -o -path "./dirname/*"
:
find . -name ''*.js'' -not /( -path "./dir1" -o -path "./dir2/*" -o -path "./dir3/*"/)
Pero tal vez debería usar una expresión regular , si hay muchos directorios para excluir.
Para lo que necesitaba, funcionó así, encontrando landscape.jpg
en todos los servidores comenzando desde la raíz y excluyendo la búsqueda en el directorio /var
:
find / -maxdepth 1 -type d | grep -v /var | xargs -I ''{}'' find ''{}'' -name landscape.jpg
find / -maxdepth 1 -type d
enumera todos los irectorios en /
grep -v /var
excluye `/ var ''de la lista
xargs -I ''{}'' find ''{}'' -name landscape.jpg
ejecute cualquier comando, como find
con cada directorio / resultado de la lista
Para una solución de trabajo (probada en Ubuntu 12.04 (Precise Pangolin)) ...
find ! -path "dir1" -iname "*.mp3"
buscará archivos MP3 en la carpeta actual y las subcarpetas, excepto en la subcarpeta dir1.
Utilizar:
find ! -path "dir1" ! -path "dir2" -iname "*.mp3"
... excluir dir1 y dir2
Para usuarios de FreeBSD :
find . -name ''*.js'' -not -path ''*exclude/this/dir*''
Prefiero la notación -not
... es más legible:
find . -name ''*.js'' -and -not -path directory
Puede utilizar la opción de podar para lograr esto. Como en por ejemplo:
find ./ -path ./beta/* -prune -o -iname example.com -print
O la opción inversa grep "grep -v":
find -iname example.com | grep -v beta
Puede encontrar instrucciones detalladas y ejemplos en Linux. El comando de búsqueda excluye directorios de la búsqueda .
Si -prune
no funciona para usted, esto:
find -name "*.js" -not -path "./directory/*"
Si los directorios de búsqueda tienen patrón (en mi caso la mayoría de las veces); Simplemente puedes hacerlo como abajo:
find ./n* -name "*.tcl"
En el ejemplo anterior; Busca en todos los subdirectorios comenzando con "n".
Una opción sería excluir todos los resultados que contienen el nombre del directorio con grep. Por ejemplo:
find . -name ''*.js'' | grep -v excludeddir
Utilice el modificador de poda, por ejemplo, si desea excluir el directorio misc
, simplemente agregue una -path ./misc -prune -o
a su comando de búsqueda:
find . -path ./misc -prune -o -name ''*.txt'' -print
Aquí hay un ejemplo con varios 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 en los criterios -path dir1 -o -path dir2 -o -path dir3
(si es dir1 o dir2 o dir3), ANDed con type -d
. Otra acción es -o print
, solo imprimir.
Utilice la opción -prune. Entonces, algo como:
find . -type d -name proc -prune -o -name ''*.js''
El ''-type d -name proc -prune'' solo busca los directorios llamados proc para excluir.
El ''-o'' es un operador ''O''.
Utilice mejor la acción exec
que el bucle for
:
find . -path "./dirtoexclude" -prune /
-o -exec java -jar config/yuicompressor-2.4.2.jar --type js ''{}'' -o ''{}'' /;
El exec ... ''{}'' ... ''{}'' /;
se ejecutará una vez por cada archivo coincidente, reemplazando las llaves ''{}''
con el nombre del archivo actual.
Observe que las llaves están entre comillas simples para protegerlas de la interpretación como puntuación de script de shell * .
Notas
* De la sección EJEMPLOS de la página de manual find (GNU findutils) 4.4.2
-prune
definitivamente funciona y es la mejor respuesta porque evita descender a la dir que desea excluir. -not -path
que aún busca en el directorio excluido, simplemente no imprime el resultado, lo que podría ser un problema si el directorio excluido está montado en el volumen de la red o no tiene permisos.
La parte difícil es que el find
es muy particular sobre el orden de los argumentos, por lo que si no los entiende correctamente, es posible que su comando no funcione. El orden de los argumentos es generalmente como tal:
find {path} {options} {action}
{path}
: Ponga primero todos los argumentos relacionados con la ruta, como . -path ''./dir1'' -prune -o
. -path ''./dir1'' -prune -o
{options}
: Tengo el mayor éxito al poner -name, -iname, etc
como la última opción en este grupo. Por ejemplo, -type f -iname ''*.js''
{action}
: querrás agregar -print
cuando -prune
Aquí hay un ejemplo de trabajo:
# setup test
mkdir dir1 dir2 dir3
touch dir1/file.txt; touch dir1/file.js
touch dir2/file.txt; touch dir2/file.js
touch dir3/file.txt; touch dir3/file.js
# search for *.js, exclude dir1
find . -path ''./dir1'' -prune -o -type f -iname ''*.js'' -print
# search for *.js, exclude dir1 and dir2
find . /( -path ''./dir1'' -o -path ''./dir2'' /) -prune -o -type f -iname ''*.js'' -print
how-to-use-prune-option-of-find-in-sh es una excelente respuesta de Laurence Gonsalves sobre cómo funciona la -prune
.
Y aquí está la solución genérica:
find /path/to/search /
-type d /
/( -path /path/to/search/exclude_me /
-o /
-name exclude_me_too_anywhere /
/) /
-prune /
-o /
-type f -name ''*/.js'' -print
Para evitar escribir /path/to/seach/
varias veces, envuelva la find
en un par pushd .. popd
.
pushd /path/to/search; /
find . /
-type d /
/( -path ./exclude_me /
-o /
-name exclude_me_too_anywhere /
/) /
-prune /
-o /
-type f -name ''*/.js'' -print; /
popd
find . -name ''*.js'' -/! -name ''glob-for-excluded-dir'' -prune
find -name ''*.js'' -not -path ''./node_modules/*'' -not -path ''./vendor/*''
parece funcionar igual que
find -name ''*.js'' -not /( -path ''./node_modules/*'' -o -path ''./vendor/*'' /)
y es más fácil de recordar IMO.