Por que o find às vezes corresponde ao argumento do caminho da linha de comando?

9

No Linux,

cd /tmp
mkdir foo; cd foo

Agora, executando

find . -name 'foo'

não fornece saída. Considerando que executando

find /tmp/foo -name 'foo'

Dá a saída /tmp/foo , o que não faz sentido para mim. Alguém pode explicar por quê?

    
por York 10.01.2016 / 17:13

5 respostas

15

find atravessa a (s) árvore (s) de diretórios especificada e avalia a expressão fornecida para cada arquivo encontrado. A travessia começa no caminho dado. Veja um resumo de como o find . -name foo opera:

  • Primeiro caminho na linha de comando: .
    • O nome base ( . ) corresponde ao padrão foo ? Não, então não faça nada.
      Acontece que /tmp/foo é outro nome para o mesmo diretório. Mas find não sabe disso (e não deve tentar descobrir).
    • O caminho é um diretório? Sim, então atravesse-a. Enumere as entradas em . e, para cada entrada, execute o processo de percurso.
      • O diretório está vazio: não contém nenhuma entrada diferente de . e .. , que find não percorre recursivamente. Então o trabalho está terminado.

e find /tmp/foo :

  • Primeiro caminho na linha de comando: /tmp/foo
    • O nome base ( foo ) corresponde ao padrão foo ? Sim, então a condição corresponde.
      • Não há ações associadas a essa condição, portanto, execute a ação padrão, que é imprimir o caminho.
    • O caminho é um diretório? Sim, então atravesse-a. Enumere as entradas em /tmp/foo e, para cada entrada, execute o processo de percurso.
      • O diretório está vazio: não contém nenhuma entrada diferente de . e .. , que find não percorre recursivamente. Então o trabalho está terminado.

Acontece que . e /tmp/foo são o mesmo diretório, mas isso não é suficiente para garantir que find tenha o mesmo comportamento em ambos. O comando find tem maneiras de distinguir entre caminhos para o mesmo arquivo; o predicado -name é um deles. find /tmp/foo -name foo corresponde ao diretório inicial, bem como a qualquer arquivo abaixo dele, chamado foo . find . -name . corresponde apenas ao diretório inicial ( . nunca pode ser encontrado durante uma travessia recursiva).

    
por 11.01.2016 / 01:09
5

Não há normalização dos argumentos da linha de comando antes que os testes sejam aplicados. Assim, os resultados diferem dependendo do caminho usado (se links simbólicos estiverem envolvidos):

cd /tmp
mkdir foo
ln -s foo bar
find /tmp/foo -name foo
find /tmp/bar -name foo

No "seu caso", ambas as chamadas dariam o mesmo resultado que poderia ser (mais) confuso. Você pode usar -mindepth 1 se quiser que os pontos iniciais sejam ignorados (podem ser não-POSIX).

    
por 10.01.2016 / 17:38
2

(gnu) find mostra todas as correspondências encontradas no caminho fornecido ao comando porque ele inicia sua comparação com os argumentos da linha de comando, descendo mais para dentro da estrutura de diretórios (assim, -maxdepth 0 confina os testes ao nível base ou somente os argumentos de linha de comando, enquanto -mindepth 1 ignora os argumentos de linha de comando como man find explica). Esta é a razão pela qual find /tmp/foo -name 'foo' produzirá uma correspondência mesmo se o próprio diretório estiver vazio.

find . -name 'foo' , por outro lado, não produzirá nenhum resultado porque . (ponto) é um arquivo especial que age como um hardlink para o mesmo inode que /tmp/foo - é como um nome de arquivo separado (embora especial) e não um link simbólico ou uma expressão que é submetida à expansão de nome de caminho pelo shell. Portanto, o primeiro teste aplicado pelos argumentos de linha de comando no exemplo fornecido não mostrará nenhuma correspondência, pois . de fato não corresponde ao padrão de nome definido em -name 'foo' . O /tmp/foo/. também não é realizado, pois um teste para um padrão -name é executado apenas no nome de base do caminho (consulte man find ), que aqui novamente é . .

Embora esse comportamento possa não ser esperado ou parecer intuitivo do ponto de vista do usuário (e sim, também me confundi no início), ele não constitui um bug, mas corresponde à lógica e à funcionalidade descritas no manual e no info páginas para (gnu) find.

    
por 10.01.2016 / 23:40
0

Eu tentei comentar a resposta de Gilles, mas foi muito longo para caber em um comentário. Por esse motivo, estou colocando isso como uma resposta à minha própria pergunta.

A explicação de Gilles (e a de Shevek também) é clara e faz sentido. O ponto chave aqui é que não só find tenta combinar os nomes dos arquivos dentro dos caminhos dados (recursivamente), mas também tenta combinar o nome base dos próprios caminhos.

Por outro lado, existe alguma prova de que esta é a maneira que o find deve funcionar, ao invés de ser um bug? Na minha opinião, seria melhor se isso não tornasse os resultados inconsistentes para find . e find ABSOLUTE-PATH , porque a inconsistência é sempre confusa e poderia desperdiçar tempo demais com o desenvolvedor tentando descobrir "o que estava errado". No meu caso, eu estava escrevendo um roteiro e o caminho foi tirado de uma variável. Então, para que meu script funcione corretamente, o que posso pensar em fazer é escrever find $path/. -name 'pattern' .

Finalmente, acho que obter resultados consistentes para find pode ser obtido substituindo sempre o . pelo diretório atual antes de continuar.

    
por 11.01.2016 / 13:31
-1

Não há nenhum objeto chamado foo no diretório em que você iniciou a pesquisa relativa.

Você está correto em assumir que gfind está com bugs quando relata /tmp/foo ao usar o nome absoluto como um diretório inicial.

Gfind tem vários desvios do padrão, parece que você encontrou outro. Quando você gosta de uma solução mais compatível com o padrão, recomendo sfind que faz parte do schilytools .

-name aplica-se aos resultados da pesquisa de diretórios. Nenhum dos dois casos mencionados retornará uma entrada de diretório foo de uma operação readdir() .

    
por 10.01.2016 / 18:17

Tags