Como usar a opção '-Prune' de 'encontrar' em SH?
Pergunta
Eu não entendo muito bem o exemplo dado do man find
, alguém pode me dar alguns exemplos e explicações? Posso combinar expressão regular nele?
A pergunta mais detalhada é assim:
Escreva um script de shell, changeall
, que tem uma interface como changeall [-r|-R] "string1" "string2"
. Ele encontrará todos os arquivos com um sufixo de .h
, .C
, .cc
, ou .cpp
e mudar todas as ocorrências de string1
para string2
. -r
é opção para permanecer apenas no diretor atual ou incluir o subdir.
NOTA:
- Para um caso não recursivo,
ls
não é permitido, só podíamos usarfind
esed
. - eu tentei
find -depth
Mas não foi apoiado. É por isso que eu estava me perguntando se-prune
poderia ajudar, mas não entendeu o exemplo deman find
.
EDIT2: Eu estava fazendo tarefas, não fiz perguntas em ótimos detalhes porque gostaria de terminar sozinho. Desde que eu já fiz isso e entregue, agora posso declarar toda a questão. Além disso, consegui terminar a tarefa sem usar -prune
, mas gostaria de aprender de qualquer maneira.
Solução
O que eu achei confuso sobre -prune
é isso é uma ação (como -print
), não um teste (como -name
). Altera a lista de "tarefas", mas sempre retorna verdadeiro.
O padrão geral para usar -prune
é isto:
find [path] [conditions to prune] -prune -o \
[your usual conditions] [actions to perform]
Você praticamente sempre quer o -o
(lógico ou) imediatamente após -prune
, porque a primeira parte do teste (até e incluindo -prune
) retornará falso Para as coisas que você realmente deseja (ou seja: as coisas que você não quer podar).
Aqui está um exemplo:
find . -name .snapshot -prune -o -name '*.foo' -print
Isso encontrará os arquivos "*.foo" que não estão em diretórios .snapshot ". Neste exemplo, -name .snapshot
compõe o [conditions to prune]
, e -name '*.foo' -print
é [your usual conditions]
e [actions to perform]
.
Anotações importantes:
Se tudo o que você deseja fazer é imprimir os resultados que você pode estar acostumado a deixar de fora o
-print
ação. Você geralmente não quero fazer isso ao usar-prune
.O comportamento padrão do encontro é "e" o inteira expressão com o
-print
ação se não houver outras ações senão-prune
(Ironicamente) no final. Isso significa que escrever isso:find . -name .snapshot -prune -o -name '*.foo' # DON'T DO THIS
é equivalente a escrever isso:
find . \( -name .snapshot -prune -o -name '*.foo' \) -print # DON'T DO THIS
O que significa que também imprimirá o nome do diretório que você está podando, o que geralmente não é o que você deseja. Em vez disso, é melhor especificar explicitamente o
-print
ação se é isso que você deseja:find . -name .snapshot -prune -o -name '*.foo' -print # DO THIS
Se a sua "condição usual" corresponder aos arquivos que também correspondem à sua condição de ameixa, esses arquivos irão não ser incluído na saída. A maneira de consertar isso é adicionar um
-type d
predicar à sua condição de ameixa.Por exemplo, suponha que queríamos podar qualquer diretório que começou com
.git
(Isso é reconhecidamente um tanto artificial - normalmente você só precisa remover a coisa nomeada exatamente.git
), mas, além disso, queria ver todos os arquivos, incluindo arquivos como.gitignore
. Você pode tentar o seguinte:find . -name '.git*' -prune -o -type f -print # DON'T DO THIS
Isso seria não incluir
.gitignore
na saída. Aqui está a versão fixa:find . -type d -name '.git*' -prune -o -type f -print # DO THIS
Dica extra: se você está usando a versão GNU de find
, a página do Texinfo para find
tem uma explicação mais detalhada do que sua manpra (como é verdadeira para a maioria dos utilitários da GNU).
Outras dicas
Cuidado que -Rune não impede descer em algum diretório como alguns disseram. Isso evita descer em diretórios que correspondem ao teste ao qual é aplicado. Talvez alguns exemplos ajudem (consulte o fundo para um exemplo regex). Desculpe por isso ser tão demorado.
$ find . -printf "%y %p\n" # print the file type the first time FYI
d .
f ./test
d ./dir1
d ./dir1/test
f ./dir1/test/file
f ./dir1/test/test
d ./dir1/scripts
f ./dir1/scripts/myscript.pl
f ./dir1/scripts/myscript.sh
f ./dir1/scripts/myscript.py
d ./dir2
d ./dir2/test
f ./dir2/test/file
f ./dir2/test/myscript.pl
f ./dir2/test/myscript.sh
$ find . -name test
./test
./dir1/test
./dir1/test/test
./dir2/test
$ find . -prune
.
$ find . -name test -prune
./test
./dir1/test
./dir2/test
$ find . -name test -prune -o -print
.
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2
$ find . -regex ".*/my.*p.$"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test/myscript.pl
$ find . -name test -prune -regex ".*/my.*p.$"
(no results)
$ find . -name test -prune -o -regex ".*/my.*p.$"
./test
./dir1/test
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test
$ find . -regex ".*/my.*p.$" -a -not -regex ".*test.*"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
$ find . -not -regex ".*test.*" .
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2
Normalmente, da maneira nativa, fazemos as coisas no Linux e a maneira como pensamos ser da esquerda para a direita.
Então você iria escrever o que está procurando primeiro:
find / -name "*.php"
Então você provavelmente pressiona Enter e percebe que está recebendo muitos arquivos de diretórios que deseja não. Vamos excluir /mídia para evitar pesquisar suas unidades montadas.
Agora você deve apenas anexar o seguinte ao comando anterior:
-print -o -path '/media' -prune
Então, o comando final é:
find / -name "*.php" -print -o -path '/media' -prune
............... | <--- incluir ---> | .................... | <- -------- exclude ---------> |
Eu acho que essa estrutura é muito mais fácil e se correlaciona com a abordagem correta
Adicionando aos conselhos dados em outras respostas (não tenho representante para criar respostas) ...
Ao combinar -prune
Com outras expressões, há uma diferença sutil de comportamento, dependendo de quais outras expressões são usadas.
O exemplo do @Laurence Gonsalves encontrará os arquivos "*.foo" que não estão abaixo ".snapshot" Diretórios:-
find . -name .snapshot -prune -o -name '*.foo' -print
No entanto, essa mão curta ligeiramente diferente, talvez inadvertidamente, também listará o .snapshot
Diretório (e qualquer diretório .SNAPSHOT aninhado):-
find . -name .snapshot -prune -o -name '*.foo'
O motivo é (de acordo com a manpra no meu sistema):-
Se a expressão dada não contiver nenhuma das primárias -exec, -ls, -ok ou -print, a expressão dada é efetivamente substituída por:
(dado_expression) -print
Ou seja, o segundo exemplo é o equivalente a entrar no seguinte, modificando assim o agrupamento de termos:-
find . \( -name .snapshot -prune -o -name '*.foo' \) -print
Isso foi visto pelo menos no Solaris 5.10. Tendo usado vários sabores de *nix por aproximadamente 10 anos, procurei recentemente por um motivo pelo qual isso ocorre.
Prune é um não recurso em nenhum interruptor de diretório.
Da página do homem
Se -profundidade não é dada, verdadeira; Se o arquivo for um diretório, não desça nele. Se a profundidade é dada, falsa; Sem efeito.
Basicamente, ele não será despertado em nenhum diretório.
Veja este exemplo:
Você tem os seguintes diretórios
- /home/test2
- /home/test2/test2
Se você correr find -name test2
:
Ele retornará os dois diretórios
Se você correr find -name test2 -prune
:
Ele retornará apenas/home/test2, pois não descerá para/home/test2 para encontrar/home/test2/test2
Não sou especialista nisso (e esta página foi muito útil junto com http://mywiki.wooledge.org/usingfind)
Acabei de perceber -path
é para um caminho que Combina totalmente a string/caminho que vem logo depois find
(.
em esses exemplos) onde -name
corresponde a todos os nomes de base.
find . -path ./.git -prune -o -name file -print
bloqueia o diretório .git em seu diretório atual ( Como sua descoberta em .
)
find . -name .git -prune -o -name file -print
Bloqueia todos os subdiretos .git recursivamente.
Note o ./
é extremamente importante !! -path
deve corresponder a um caminho ancorado a .
ou o que vier logo após encontrar Se você conseguir partidas sem isso (do outro lado da AR '-o
') Provavelmente não está sendo podado! Eu não tinha conhecimento disso e me colocou de usar -Path quando é ótimo quando você não quer podar todo o subdiretório com o mesmo nome de base: D
Mostre tudo, incluindo Dir em si, mas não seu longo conteúdo chato:
find . -print -name dir -prune
Se você ler todas as boas respostas aqui, meu entendimento agora é que o seguinte retornará os mesmos resultados:
find . -path ./dir1\* -prune -o -print
find . -path ./dir1 -prune -o -print
find . -path ./dir1\* -o -print
#look no prune at all!
Mas O último levará muito mais tempo, pois ainda procura tudo no DIR1. Eu acho que a verdadeira questão é como -or
Resultados indesejados sem realmente pesquisá -los.
Então eu acho que ameixa significa não decentes partidas anteriores, mas marque como feito ...
http://www.gnu.org/software/findutils/manual/html_mono/find.html"No entanto, isso não se deve ao efeito da ação '-Prune' (que apenas impede a descida adicional, não garante que ignoremos esse item). Em vez disso, esse efeito se deve ao uso de '-o'. Como o lado esquerdo da condição "ou" foi bem-sucedido para ./src/emacs, não é necessário avaliar o lado direito ('-Print') para esse arquivo específico ".
find
cria uma lista de arquivos. Aplica o predicado que você forneceu a cada um e retorna aqueles que passam.
Essa ideia é isso -prune
significa que a exclusão dos resultados foi realmente confusa para mim. Você pode excluir um arquivo sem ameixa:
find -name 'bad_guy' -o -name 'good_guy' -print // good_guy
Tudo -prune
Faz é alterar o comportamento da pesquisa. Se a partida atual for um diretório, diz "Ei find
, aquele arquivo que você acabou de combinar, não desce nele ". Ele apenas remove essa árvore (mas não o próprio arquivo) da lista de arquivos para pesquisar.
Deve ser nomeado -dont-descend
.