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

9

No Linux,

cd /tmp
mkdir foo; cd foo

Agora correndo

find . -name 'foo'

não dá saída. Considerando que correr

find /tmp/foo -name 'foo'

Dá a saída /tmp/fooque não faz sentido para mim. Alguém pode explicar o porquê?

Iorque
fonte
2
encontre as buscas dentro do caminho fornecido, conforme indicado. Então, se você digitar ./, não correspondefoo
Costas
@ Costas: eu entendo isso. O que não entendo é por que fez diferença se eu der o caminho absoluto para find.
York
@York Em certas situações, não existe um comportamento sempre correto. Imagine um link simbólico com o nome barque aponta para um arquivo fooque está fora do caminho de pesquisa. Deve combinar ou não?
Hauke ​​Laging
1
Os .e /tmp/foonão são os mesmos - são dois links físicos diferentes para o mesmo diretório; find /tmp/foo/. -name 'foo'também não encontra nada.
jimmij
@jimmij: quando corro find /tmp/foo -name 'foo', estava pedindo ao bash para encontrar no diretório /tmp/fooum arquivo cujo nome é "foo". Como o diretório /tmp/fooestá vazio, ele não deve ter retornado nada. Eu não entendo por que ele retorna /tmp/foo. Por outro lado, quando eu corro find . -name 'foo', estava perguntando ao bash a mesma coisa, ou seja, encontrando um arquivo no diretório atual (que passou a ser /tmp/foo), cujo nome é 'foo', e ele não retorna nada que faça sentido.
York

Respostas:

15

findpercorre as árvores de diretórios especificadas e avalia a expressão fornecida para cada arquivo que encontra. A travessia começa no caminho especificado. Aqui está um resumo de como find . -name foofunciona:

  • 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 findnão sabe disso (e não deve tentar descobrir).
    • O caminho é um diretório? Sim, então atravesse-o. Enumere as entradas .e, para cada entrada, execute o processo de travessia.
      • O diretório está vazio: ele não contém nenhuma entrada além de .e .., que findnã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ção associada a essa condição, portanto, execute a ação padrão, que é imprimir o caminho.
    • O caminho é um diretório? Sim, então atravesse-o. Enumere as entradas /tmp/fooe, para cada entrada, execute o processo de travessia.
      • O diretório está vazio: ele não contém nenhuma entrada além de .e .., que findnão percorre recursivamente. Então o trabalho está terminado.

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

Gilles 'SO- parar de ser mau'
fonte
"ele não contém nenhuma entrada que não seja. e .., que encontrar não percorre recursivamente." Presumo que "não se recursivamente se recursivamente" se refira a "..". Se isso estiver correto, o que "transversalmente recursivamente" significaria no contexto de ".."?
Faheem Mitha
@FaheemMitha "não percorre recursivamente" se aplica a ambos .e ... (Se findpercorrido de forma .recursiva, acabaria percorrendo o diretório novamente e novamente e novamente e ...) .e ..não são apenas notações especiais, elas aparecerão como entradas de diretório.
Gilles 'SO- stop be evil' (
5

Não há normalização dos argumentos da linha de comandos antes da aplicação dos testes. Portanto, 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 pode ser (mais) confuso. Você pode usar -mindepth 1se desejar que os pontos de partida sejam ignorados (podem não ser POSIX).

Hauke ​​Laging
fonte
-mindepth 1trabalho. No entanto, ainda não entendo o porquê find . -name 'foo'e me find /tmp/foo -name 'foo'comporto de maneira diferente.
York
2

(gnu) find mostra todas as correspondências encontradas no caminho fornecido ao comando porque inicia sua comparação com os argumentos da linha de comando, descendo mais profundamente na estrutura de diretórios a partir daí (portanto, -maxdepth 0restringe os testes ao nível base ou apenas aos argumentos da linha de comando, enquanto -mindepth 1pula os argumentos da linha de comando, como man findexplica). Esta é a razão pela find /tmp/foo -name 'foo'qual produzirá uma correspondência, mesmo que o diretório em si esteja vazio.

find . -name 'foo'por outro lado, não produzirá nenhum resultado porque .(ponto) é um arquivo especial que age como um /tmp/foolink físico para o mesmo inode - é como um nome de arquivo separado (embora especial) e não como um link simbólico ou uma expressão sujeita a expansão do nome do caminho pelo shell. Portanto, o primeiro teste aplicado por find aos argumentos da linha de comando no exemplo fornecido não mostrará nenhuma correspondência, pois, na .verdade, não corresponde ao padrão de nome definido em -name 'foo'. Nem /tmp/foo/.uma vez que um teste para um -namepadrão é realizado apenas no nome da base do caminho (consulte man find), que aqui é novamente ..

Embora esse comportamento possa não ser esperado ou parecer intuitivo da perspectiva do usuário (e sim, também me confundiu a princípio), ele não constitui um bug, mas corresponde à lógica e à funcionalidade descritas nas páginas de manual e informações de ( gnu) encontrar.

Shevek
fonte
0

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

A explicação de Gilles (e a de Shevek também) é clara e faz sentido. O ponto principal aqui é que não apenas findtenta corresponder os nomes dos arquivos dentro dos caminhos especificados (recursivamente), mas também tenta combinar o nome base dos caminhos especificados.

Por outro lado, há alguma prova de que esse é o caminho findque deve funcionar, em vez de ser um bug? Na minha opinião, seria melhor se isso não tornasse os resultados inconsistentes find .e find ABSOLUTE-PATHporque a inconsistência é sempre confusa e poderia desperdiçar o desenvolvedor muito tempo tentando descobrir "o que estava errado". No meu caso, eu estava escrevendo um script e o caminho foi tomado a partir de uma variável. Portanto, para que meu script funcione corretamente, o que posso pensar em fazer é escrever find $path/. -name 'pattern'.

Finalmente, acho que é possível obter resultados consistentes findsempre substituindo-o .pelo diretório atual antes de prosseguir.

Iorque
fonte
-1

Não há nenhum objeto nomeado foono diretório em que você iniciou a pesquisa relativa.

Você está correto ao supor que gfindhá erros quando ele relata /tmp/fooao usar o nome absoluto como um diretório inicial.

Gfindtem vários desvios do padrão, parece que você encontrou outro. Quando você gosta de uma solução compatível mais padrão, recomendo sfindque faça parte do schilytools.

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

esperto
fonte
Eu suspeito que isso seja um bug também. Aqui está a saída de find --version: find (GNU findutils) 4.4.2 Copyright (C) 2007 Free Software Foundation, Inc. Licença GPLv3 +: GNU GPL versão 3 ou posterior < gnu.org/licenses/gpl.html >
York
Bem, verifiquei seu exemplo de comando com find(certificado POSIX) gfinde sfind; o problema só existe quando você usa gfind. No Linux, gfindnão é instalado com o nome nativo, mas com o nome find.
schily
3
Está correto para find /tmp/foo -name fooa saída /tmp/foo. (Cc @York) Esse é o comportamento do OpenBSD find, GNU find, BusyBox find, Solaris finde qualquer outro compatível com POSIX find("O primário avaliará como verdadeiro se o nome da base do nome do arquivo que está sendo examinado corresponder ao padrão (...)" - quando o nome do arquivo é passado como um operando de caminho, nenhuma readdirchamada está envolvida).
Gilles 'SO- stop be evil'