Use o comando find, mas exclua arquivos em dois diretórios

86

Desejo localizar arquivos que terminam com _peaks.bed, mas excluir arquivos nas pastas tmpe scripts.

Meu comando é assim:

 find . -type f \( -name "*_peaks.bed" ! -name "*tmp*" ! -name "*scripts*" \)

Mas não funcionou. Os arquivos tmpe scriptpasta ainda serão exibidas.

Alguém tem ideias sobre isso?

Hanfei Sun
fonte

Respostas:

190

Veja como você pode especificar isso com find:

find . -type f -name "*_peaks.bed" ! -path "./tmp/*" ! -path "./scripts/*"

Explicação:

  • find . - Comece a encontrar a partir do diretório de trabalho atual (recursivamente por padrão)
  • -type f- Especifique findque você deseja apenas arquivos nos resultados
  • -name "*_peaks.bed" - Procure por arquivos com o nome terminando em _peaks.bed
  • ! -path "./tmp/*" - Exclua todos os resultados cujo caminho comece com ./tmp/
  • ! -path "./scripts/*" - Exclua também todos os resultados cujo caminho comece com ./scripts/

Testando a solução:

$ mkdir a b c d e
$ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
$ find . -type f ! -path "./a/*" ! -path "./b/*"

./d/4
./c/3
./e/a
./e/b
./e/5

Você chegou bem perto, a -nameopção considera apenas o nome de base, onde as -pathconsidera todo o caminho =)

Sampson-Chen
fonte
Bom trabalho. No entanto, você esqueceu uma das coisas que o OP queria, localizar os arquivos que terminam com _peaks.bed.
alex
2
Ele usa uma série de extensões no GNU find, mas como a questão está marcada como Linux, isso não é um problema. Boa resposta.
Jonathan Leffler
1
Uma breve observação: se você usar .em seu prompt de localização inicial, terá que usá-lo em cada caminho que excluir. A correspondência de caminho é bastante restrita, não faz pesquisas difusas. Então, se você usar, find / -type f -name *.bed" ! -path "./tmp/"não vai funcionar. você precisa ter ! -path "/tmp"que torná-lo feliz.
peelman
3
É importante observar que o * é importante. $ ! -path "./directory/*"
Thomas Bennett
3
De acordo com as páginas do manual: "Para ignorar uma árvore de diretório inteira, use em -prunevez de verificar todos os arquivos na árvore." Se os diretórios excluídos forem muito profundos ou tiverem toneladas de arquivos e você se preocupa com o desempenho, use a -pruneopção.
thdoan
8

Aqui está uma maneira de fazer isso ...

find . -type f -name "*_peaks.bed" | egrep -v "^(./tmp/|./scripts/)"
alex
fonte
2
Isso tem o mérito de funcionar com qualquer versão do find, em vez de apenas com o GNU find. No entanto, a questão é marcada Linux para que não seja crítica.
Jonathan Leffler
2

Usar

find \( -path "./tmp" -o -path "./scripts" \) -prune -o  -name "*_peaks.bed" -print

ou

find \( -path "./tmp" -o -path "./scripts" \) -prune -false -o  -name "*_peaks.bed"

ou

find \( -path "./tmp" -path "./scripts" \) ! -prune -o  -name "*_peaks.bed"

A ordem é importante. Ele avalia da esquerda para a direita. Sempre comece com a exclusão do caminho.

Explicação

Não use -not(ou !) para excluir o diretório inteiro. Use -prune. Conforme explicado no manual:

−prune    The primary shall always evaluate as  true;  it
          shall  cause  find  not  to descend the current
          pathname if it is a directory.  If  the  −depth
          primary  is specified, the −prune primary shall
          have no effect.

e no manual de localização do GNU:

-path pattern
              [...]
              To ignore  a  whole
              directory  tree,  use  -prune rather than checking
              every file in the tree.

Na verdade, se você usar -not -path "./pathname", find avaliará a expressão para cada nó em "./pathname".

encontrar expressões são apenas avaliações de condições.

  • \( \)- operação de grupos (você pode usar -path "./tmp" -prune -o -path "./scripts" -prune -o, mas é mais detalhado).
  • -path "./script" -prune- se -pathretorna verdadeiro e é um diretório, retorna verdadeiro para aquele diretório e não desce para ele.
  • -path "./script" ! -prune- avalia como (-path "./script") AND (! -prune). Ele reverte o "sempre verdadeiro" da poda para sempre falso. Isso evita a impressão "./script"como um fósforo.
  • -path "./script" -prune -false- como -prunesempre retorna true, você pode seguir com -falsepara fazer o mesmo que !.
  • -o- Operador OR. Se nenhum operador for especificado entre duas expressões, o padrão é o operador AND.

Portanto, \( -path "./tmp" -o -path "./scripts" \) -prune -o -name "*_peaks.bed" -printé expandido para:

[ (-path "./tmp" OR -path "./script") AND -prune ] OR ( -name "*_peaks.bed" AND print )

A impressão é importante aqui porque sem ela é expandida para:

{ [ (-path "./tmp" OR -path "./script" )  AND -prune ]  OR (-name "*_peaks.bed" ) } AND print

-printé adicionado por find - é por isso que na maioria das vezes, você não precisa adicioná-lo em sua expressão. E como -pruneretorna true, ele irá imprimir "./script" e "./tmp".

Não é necessário nas outras porque mudamos -prunepara sempre retornar falso.

Dica: você pode usar find -D opt expr 2>&1 1>/dev/nullpara ver como está otimizado e expandido,
find -D search expr 2>&1 1>/dev/nullpara ver qual caminho está marcado.

f380cedric
fonte
0

Tente algo como

find . \( -type f -name \*_peaks.bed -print \) -or \( -type d -and \( -name tmp -or -name scripts \) -and -prune \)

e não se surpreenda se eu errar um pouco. Se o objetivo for um exec (em vez de impressão), basta substituí-lo no lugar.

DrC
fonte
0

para mim, esta solução não funcionou em um exec de comando com find, realmente não sei por que, então minha solução é

find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

Explicação: o mesmo que sampson-chen um com a adição de

-prune - ignora o caminho de procedimento de ...

-o - Então, se não houver correspondência, imprime os resultados (remova os diretórios e imprima os resultados restantes)

18:12 $ mkdir a b c d e
18:13 $ touch a/1 b/2 c/3 d/4 e/5 e/a e/b
18:13 $ find . -type f -path "./a/*" -prune -o -path "./b/*" -prune -o -exec gzip -f -v {} \;

gzip: . is a directory -- ignored
gzip: ./a is a directory -- ignored
gzip: ./b is a directory -- ignored
gzip: ./c is a directory -- ignored
./c/3:    0.0% -- replaced with ./c/3.gz
gzip: ./d is a directory -- ignored
./d/4:    0.0% -- replaced with ./d/4.gz
gzip: ./e is a directory -- ignored
./e/5:    0.0% -- replaced with ./e/5.gz
./e/a:    0.0% -- replaced with ./e/a.gz
./e/b:    0.0% -- replaced with ./e/b.gz
al3x2ndru
fonte
A resposta aceita não funcionou, mas funciona. Usando ameixa find . -path ./scripts -prune -name '*_peaks.bed' -type f,. Não tenho certeza de como excluir vários diretórios. Isso também lista o diretório excluído de nível superior, embora typeseja especificado. Excluir via Grep parece mais simples, a menos que você queira usar poda para acelerar a operação de localização.
Mohnish,
Tive problemas para excluir vários diretórios também, mas os comentários acima me deram uma resposta que funcionou. Eu uso várias instâncias de '-not -path' e em cada expressão de caminho eu incluo o prefixo completo como usado no primeiro parâmetro para 'find' e finalizo cada com um asterisco (e escape de quaisquer pontos).
jetset de
0

Você pode tentar abaixo:

find ./ ! \( -path ./tmp -prune \) ! \( -path ./scripts -prune \) -type f -name '*_peaks.bed'
Jacky Jiang
fonte
2
Em uma pergunta antiga como essa (4 anos!), Você quer explicar por que essa nova resposta é melhor ou diferente, não apenas "despejar" o código.
Nic3500