Como listar todos os arquivos em um diretório, exceto aqueles com extensões especificadas?

28

Suponha que eu tenha uma pasta contendo .txt , .pdf e outros arquivos. Gostaria de listar os "outros" arquivos (ou seja, arquivos sem as extensões .txt ou .pdf ). Você tem algum conselho sobre como fazer isso?

Eu sei como listar arquivos que não têm uma determinada extensão. Por exemplo, se eu quiser listar todos os arquivos, exceto os arquivos .txt , então

find -not -iname "*.txt"

ou

ls | grep -v '\.txt$' | column

parece funcionar. Mas como posso listar tudo, exceto arquivos .txt ou arquivos .pdf ? Parece que eu preciso usar algum tipo de "ou" lógico em findou grep.

Andrew
fonte
2
Lembre-se de que o comportamento de lsvs findvs globbing pode diferir para arquivos de ponto ocultos.
Jw013
11
Outra coisa a ter em mente: findatravessará subdiretórios, como um recursivo ls. Use -maxdepth 1com findpara que ele se comporte mais ls.
Jw013
Portanto, não recursivo, basta listar os arquivos no diretório atual?
Daisy5 /

Respostas:

28

Supondo que se tenha uma versão apropriada ls, esta é possivelmente a maneira mais simples:

ls -I "*.txt" -I "*.pdf"

Se você deseja iterar em todos os subdiretórios:

ls -I "*.txt" -I "*.pdf" -R
Dejian
fonte
8
As lsopções do GNU não são portáteis.
bahamat 5/09/12
4
lsrealmente não pertence a scripts portáteis, então eu suponho que o OP esteja perguntando apenas sobre uso interativo.
Jw013 5/09/12
11
Por que ls não é portátil? O que devo usar então?
Freedo
28

Encontre suportes -o

find . ! '(' -name '*.txt' -o -name '*.pdf' ')'

Você precisa dos parênteses para corrigir a precedência. O Find faz muitas coisas; Sugiro a leitura através de sua página de manual.

Você também pode fazer um ou dentro grep(mas, na verdade, não deve analisar a saída dels )

ls | grep -Ev '\.(txt|pdf)$' | column
derobert
fonte
11
Obrigado! Por que não devo analisar a saída de ls?
Andrew
6
@ Andrew primeiro, porque é frágil (considere um nome de arquivo com uma nova linha - sim, é um nome de arquivo válido - encontre -print0 / -exec / -delete / etc. Evite esse problema); segundo, porque geralmente há uma maneira mais fácil.
Derobert
Além da página de findmanual, recomendo sinceramente os artigos sobre Unix Power Tools find, como docstore.mik.ua/orelly/unix3/upt/ch09_06.htm e docstore.mik.ua/orelly/unix3/upt/ch09_12.htm
Caractere curinga
Apenas para ajudar a decodificar a declaração para os seres humanos:find . NOT ( *.txt OR *.pdf )
wisbucky
12

Com basho globbing prolongado (ligue com shopt -s extglob), o glob !(*.txt|*.pdf)deve funcionar. Você pode passar esse glob diretamente para qualquer comando que aceite argumentos de arquivo, incluindo, entre outros ls.

jw013
fonte
11
+1, mas se você tiver subdiretórios, o conteúdo deles também será listado; use -dpara evitar isso:ls -d !(*.txt|*.pdf)
legends2k 11/11
Obrigado, eu estava olhando exatamente para a sintaxe para aceitar vários arquivos, este se encaixaria bem aqui stackoverflow.com/questions/216995/...
Freedo
6

Em zshcom extendedglob:

print -rl -- *~*.(txt|pdf)

ou

print -rl -- ^*.(txt|pdf)

Ou com kshglob(sim, isso é ksh globbing e não "bash extended globbing"):

print -rl -- !(*.txt|*.pdf)

Lembre-se, porém, de que eles também excluem arquivos de ponto.

O ksh93 possui o FIGNORErecurso (mis):

FIGNORE='@(.|..|*.txt|*.pdf)'
printf '%s\n' *
Thor
fonte
4
find /path/to/directory '!' -name '*.pdf' '!' -name '*.txt'

Isso é equivalente ao comando do operador OR, devido às leis de De Morgan .

Francesco Turco
fonte
3

Conforme sugerido por derobert, sua melhor aposta é usar find. No entanto, é possível usar o resultado em um pipeline com outros comandos.

O GNU (e alguns BSDs) findsuportam o -print0predicado que diz para imprimir o nome do arquivo terminado por um caractere NUL , que não é permitido em um nome de arquivo e garante que não haverá colisão. Outros comandos podem ser instruídos a usar o NUL como seu delimitador de entrada.

O mais importante deles é o GNU xargs, que executa o comando que você especificar e transmite a lista de arquivos como argumentos da linha de comando. Você deseja executar xargs -r0em conjunto com o find's, -print0por exemplo:

find . -type f ! \( -name \*.pdf -o -name \*.txt \) -print0 | xargs -r0 ls -ld

Isso imprime com segurança uma lista longa de diretórios de todos os arquivos pdf e txt , incluindo aqueles com espaços ou caracteres não imprimíveis no nome.

Você também pode usá-lo com o GNU da tarseguinte maneira:

tar -zcf myarchive.tar.gz --null --files-from <(
  find . -type f ! -name \*.tar.gz -print0)

Isso cria um arquivo tar.gz de todos os arquivos cujos nomes não terminam em .tar.gz

rsynctambém aceita arquivos delimitados por nulo com o -0parâmetro, assim como vários outros. Mas xargsé a cola que você costuma usar para esse tipo de objetivo. Ou esse ou findo -execrecurso.

tylerl
fonte
O tarexemplo de comando é bom. Para a maioria dos propósitos, porém, -execé um ajuste melhor.
Curinga
1

Se você não possui um subdiretório

ls !(*.pdf|*.txt)

também deve funcionar!

Mas

ls -I "*.pdf" -I "*.txt"

é o caminho comum.

abu_bua
fonte
0

Como complemento, se você usar um shell compatível com bash, poderá usar a variável GLOBIGNORE para excluir resultados da correspondência de padrões. Do homem:

   GLOBIGNORE
          A colon-separated list of patterns defining the set of filenames
          to be ignored by pathname expansion.  If a filename matched by a
          pathname  expansion  pattern also matches one of the patterns in
          GLOBIGNORE, it is removed from the list of matches.

No seu caso particular:

sh$ (GLOBIGNORE='*.pdf:*.txt'; ls -d *)

Observe que eu executo esse comando como sub-shell (usando parênteses) para não alterar a variável de ambiente GLOBIGNORE do meu shell interativo.

Sylvain Leroux
fonte