find: prune no ignora la ruta especificada

13

Necesito excluir .gitde mi findbúsqueda. Para lograr eso, estoy usando el -path ./.git -pruneinterruptor:

$ find . -path ./.git -prune -o \( -type f -o -type l -o -type d \) | grep '.git'
./.git

Sin embargo, aunque esto omite el contenido del directorio .git, enumera el directorio en sí. Funciona cuando agrego-path ./.git -prune -o -print -a

find . -path ./.git -prune -o -print -a \( -type f -o -type l -o -type d \) | grep '.git'

¿Por qué es esto necesario? Pensé que ambos comandos deberían tener la misma salida. La segunda sintaxis es bastante fea.

Martin Vegter
fuente
44
Necesita un -print, de lo contrario, lo implícito se -printaplica a toda la condición
Stéphane Chazelas
1
Ver también unix.stackexchange.com/q/102191/22565
Stéphane Chazelas

Respuestas:

3

La manpágina para findda:

-prune True; if the file is a directory, do not  descend  into  it.  If
      -depth  is  given,  false;  no  effect.  Because -delete implies
      -depth, you cannot usefully use -prune and -delete together.

Entonces, en el primer ejemplo, no es así que -path ./.git -prunesea ​​falso y, por lo tanto -print, no se llamaría a la acción predeterminada ( ), por lo tanto, se imprime la línea.

Timo
fuente
22

Estaba confundido en cuanto a por qué los directorios podados también estaban siendo impresos por el findcomando, y algunos otros detalles intrincados de cómo -prunefuncionaba, pero pude resolverlo con algunos ejemplos.

Para ejecutar los siguientes ejemplos, cree los siguientes directorios y archivos.

mkdir aa
mkdir bb
touch file1
touch aa/file1
touch bb/file3

Para crear esta estructura:

$ tree

.
├── aa
   └── file1
├── bb
   └── file3
└── file1

Ahora use find para buscar directorios con nombre aa. No hay problema aqui.

$ find . -type d -name aa
./aa

Busque todos los directorios que no sean aa y obtenemos el directorio actual .y ./bb, que también tiene sentido.

$ find . -type d ! -name aa
.
./bb

Hasta ahora todo bien, pero cuando usamos -prune, find devuelve el directorio que estamos podando, lo que inicialmente me confundió porque esperaba que devolviera todos los otros directorios y no el que se estaba podando.

$ find . -type d -name aa -prune
./aa

La razón por la que devuelve el directorio que se está podando se explica, no en la -prunesección de las páginas del manual como se indica en la respuesta de Timo , sino en la EXPRESSIONSsección:

Si la expresión no contiene otras acciones -prune,  -printse realiza en todos los archivos para los que la expresión es verdadera.

lo que significa que, dado que la expresión coincide con el aanombre del directorio, la expresión se evaluará como verdadera y se imprimirá porque find agrega implícitamente -printa al final de todo el comando. Sin -printembargo, no agregará a si agrega deliberadamente la acción -o -printal final usted mismo:

find . -type d -name aa -prune -o -print
.
./file1
./bb
./bb/file3

Aquí el comando find NO agrega más un implícito -print, por lo que el directorio que estamos podando ( aa) no se imprimirá.

Entonces, finalmente, si agregamos una cláusula que busca archivos con un patrón de nombre de archivo file*después de -o, entonces debe colocar un -printal final de esa segunda cláusula como esta:

find . \( -type d -name aa -prune \) -o \( -type f -name 'file*' -print \)
./file1
./bb/file3

La razón por la que esto funciona es la misma: si no coloca un -printen la segunda cláusula, ya que no hay otra acción que no sea la -pruneacción, find agregará -printautomáticamente al final del comando, haciendo que la -prunecláusula imprima el directorio podado:

find . \( \( -type d -name aa -prune \) -o \( -type f -name 'file*' \) \) -print
./aa
./file1
./bb/file3

En general, debe colocar el -printcomando en la segunda cláusula. Si lo coloca en el medio como lo hizo el póster original, no funcionará correctamente porque los archivos que se están podando se imprimirán de inmediato y la segunda cláusula no tendrá la oportunidad de elegir los archivos que desea:

find . \( -type d -name aa -prune -o -print \) -o \( -type f -name 'file*' \)
.
./file1
./bb
./bb/file3

Así que desafortunadamente, el póster original recibió el comando incorrecto arriba al colocarlo -printen el lugar equivocado. Podría haber funcionado para su caso específico, pero no funciona en el caso general.

Hay miles de personas que tienen dificultades para entender cómo -prunefunciona. La findpágina de manual debe actualizarse para evitar la interminable confusión mundial sobre este comando.

Jerry Marbas
fuente
Mi página de manual (Arch Linux find-4.6.0) dice: "Si la expresión completa no contiene otras acciones que no sean -prune o -print, -print se realiza en todos los archivos para los que la expresión completa es verdadera". Pero funciona como lo describe.
x-yuri
0

de la manpágina,

Para ignorar un árbol de directorios completo, use -prune en lugar de verificar cada archivo del árbol. Por ejemplo, para omitir el directorio `src / emacs 'y todos los archivos y directorios debajo de él, e imprimir los nombres de los otros archivos encontrados, haga algo como esto:

find . -path ./src/emacs -prune -o -print

Entonces, puede ser que pueda usar:

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

Esto no enumerará el .gitdirectorio.

para especificar un nombre de archivo, puede hacer esto:

find . -path ./.git -prune , -name filename

Tenga en cuenta el ,operador de coma.

jianyongli
fuente
-1

Aquí hay una alternativa rápida y sucia:

find | fgrep -v /.git

Simplemente no puedo recordar findla sintaxis compleja ...

peter.slizik
fuente