find: podar não ignora o caminho especificado

13

Preciso excluir .gitda minha findpesquisa. Para conseguir isso, estou usando o -path ./.git -pruneswitch:

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

No entanto, mesmo que isso ignore o conteúdo do diretório .git, ele lista o próprio diretório. Funciona quando adiciono-path ./.git -prune -o -print -a

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

Por que isso é necessário? Eu pensei que os dois comandos deveriam ter a mesma saída. A segunda sintaxe é bastante feia.

Martin Vegter
fonte
4
Você precisa de um -print, caso contrário, o implícito -printse aplica a toda a condição
Stéphane Chazelas
1
Veja também unix.stackexchange.com/q/102191/22565
Stéphane Chazelas

Respostas:

3

A manpágina para findfornece:

-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.

Portanto, no primeiro exemplo, não -path ./.git -pruneé falso e, portanto, a ação padrão ( -print) não seria chamada, portanto a linha é impressa.

Timo
fonte
22

Eu estava confuso sobre o motivo pelo qual os diretórios podados também estavam sendo impressos pelo findcomando e alguns outros detalhes intricados de como -prunefuncionava, mas consegui descobrir isso com alguns exemplos.

Para executar os exemplos abaixo, crie os seguintes diretórios e arquivos.

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

Para criar essa estrutura:

$ tree

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

Agora use find para procurar diretórios nomeados aa. Não tem problema aqui.

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

Procure por todos os diretórios que não sejam aa e obtemos o diretório atual .e ./bb, o que também faz sentido.

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

Até aqui tudo bem, mas quando usamos -prune, find retorna o diretório que estamos removendo, o que inicialmente me confundiu, porque eu esperava que ele retornasse todos os outros diretórios e não o que estava sendo podado.

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

O motivo pelo qual ele retorna o diretório que está sendo removido é explicado, não na -pruneseção das páginas de manual, conforme indicado na resposta do Timo , mas na EXPRESSIONSseção:

Se a expressão não contiver ações além de -prune,  -printserá executada em todos os arquivos para os quais a expressão é verdadeira.

o que significa que, como a expressão corresponde ao aanome do diretório, a expressão será avaliada como verdadeira e será impressa porque find adiciona implicitamente a -printno final de todo o comando. No -printentanto, ele não adicionará um se você propositadamente adicionar a ação -o -printaté o fim:

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

Aqui, o comando find NÃO adiciona mais um implícito -printe, portanto, o diretório que estamos removendo ( aa) não será impresso.

Então, finalmente, se adicionarmos uma cláusula que procura por arquivos com um padrão de nome de arquivo file*após -o, você deve colocar a -printno final dessa segunda cláusula, como esta:

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

O motivo pelo qual isso funciona é o mesmo: se você não inserir um -printna segunda cláusula, como não há outra ação além da -pruneação, o find adicionará um -printautomaticamente no fim do comando, fazendo com que a -prunecláusula imprima o diretório removido:

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

Em geral, você precisa colocar o -printcomando na segunda cláusula. Se você colocá-lo no meio, como o pôster original, não funcionará corretamente, pois os arquivos que serão removidos serão impressos imediatamente e a segunda cláusula não terá a chance de escolher os arquivos que deseja:

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

Infelizmente, o pôster original entendeu errado o comando acima, colocando o -printno lugar errado. Pode ter funcionado para o seu caso específico, mas não funciona no caso geral.

Existem milhares de pessoas que têm dificuldades para entender como -prunefunciona. A findpágina de manual deve ser atualizada para evitar a confusão mundial sem fim sobre esse comando.

Jerry Marbas
fonte
Minha página de manual (Arch Linux find-4.6.0) diz: "Se a expressão inteira não contiver ações além de -une ou -print, -print será executada em todos os arquivos para os quais a expressão inteira é verdadeira". Mas funciona como você descreve.
x-yuri
0

da manpágina,

Para ignorar uma árvore de diretórios inteira, use -prune em vez de verificar todos os arquivos na árvore. Por exemplo, para pular o diretório `src / emacs 'e todos os arquivos e diretórios abaixo dele, e imprimir os nomes dos outros arquivos encontrados, faça algo assim:

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

Então, pode ser que você possa usar:

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

Isso não lista o .gitdiretório.

para especificar um nome de arquivo, você pode fazer o seguinte:

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

Observe o ,operador de vírgula.

jianyongli
fonte
-1

Aqui está uma alternativa rápida e suja:

find | fgrep -v /.git

Simplesmente não consigo me lembrar findda sintaxe complexa ...

peter.slizik
fonte