Exclua todos os arquivos, exceto os arquivos com a extensão pdf, em um diretório

50

Eu tenho um diretório que contém o seguinte:

x.pdf
y.zip
z.mp3
a.pdf

Quero excluir todos os arquivos além de x.pdfe a.pdf. Como faço isso no terminal? Não há subdiretórios, portanto não há necessidade de recursão.

Starkers
fonte

Respostas:

63
cd <the directory you want>
find . -type f ! -iname "*.pdf" -delete
  • O primeiro comando o levará ao diretório em que você deseja excluir seus arquivos
  • O segundo comando excluirá todos os arquivos, exceto aqueles que terminam com .pdfin filename

Por exemplo, se houver um diretório chamado tempna sua pasta pessoal:

cd ~/temp

exclua os arquivos:

find . -type f ! -iname "*.pdf" -delete

Isso excluirá todos os arquivos, exceto xyz.pdf.

Você pode combinar esses dois comandos para:

find ~/temp -type f ! -iname "*.pdf" -delete

.é o diretório atual. !significa levar todos os arquivos, exceto os que estão .pdfno final. -type fseleciona apenas arquivos, não diretórios. -deletesignifica excluí-lo.

NOTA: este comando excluirá todos os arquivos (exceto arquivos PDF, mas incluindo arquivos ocultos) no diretório atual e em todos os subdiretórios. !deve vir antes -name. simplesmente -namevai incluir apenas .pdf, enquanto que -inameirá incluir tanto .pdfe.PDF

Para excluir apenas no diretório atual e não nos subdiretórios, adicione -maxdepth 1:

find . -maxdepth 1 -type f ! -iname "*.pdf" -delete
Edward Torvalds
fonte
Obrigado pela resposta. Você pode me ajudar a entender um pouco a sintaxe? .significa "e"? !significa "exceto" -namesignifica que você deseja excluir por um parâmetro de nome e, em seguida, -deletea ação a ser tomada ao encontrar? Então, ele procura por tudo, exceto "* .pdf", e os exclui? Ou eu entendi mal?
Jessenorton
.significa diretório atual. !significa levar todos os arquivos, exceto aquele com .pdfno final. -deletesignifica excluí-lo. estou claro agora?
Edward Torvalds
@terdon Starkers disse que não há sub-directories.wait doente editar a minha resposta a ser mais ampla
Edward Torvalds
+1 Você deve ter incluído o -maxdepth 1parâmetro para começar. Em seguida, sugira a remoção do parâmetro, caso se queira excluir recursivamente.
Tulains Córdova
3
isso trouxe à minha atenção que deveríamos usar em -inamevez de -name, ou os arquivos com .PDFuma extensão passarão.
Muru
43

Com basho globbing estendido do shell, você pode remover qualquer arquivo com extensões que não sejam o .pdfuso de

rm -- *.!(pdf)

Conforme observado por @pts, os --caracteres indicam o final de qualquer opção de comando, tornando o comando seguro no caso raro de arquivos cujos nomes começam com um -caractere.

Se você deseja excluir arquivos sem qualquer extensão, bem como aqueles com extensões diferentes .pdf, como indicado por @DennisWilliamson, você pode usar

rm -- !(*.pdf)

O globbing estendido deve ser ativado por padrão, mas se não, você pode fazê-lo usando

shopt -s extglob

Especialmente se você pretende usar isso dentro de um script, é importante observar que, se a expressão não corresponder a nada (por exemplo, se não houver arquivos que não sejam pdf no diretório), por padrão, o globo será passado sem expansão para o diretório rmcomando, resultando em um erro como

rm: cannot remove `*.!(pdf)': No such file or directory

Você pode modificar esse comportamento padrão usando a nullglobopção shell, no entanto, isso tem seu próprio problema. Para uma discussão mais aprofundada, consulte NullGlob - Greg's Wiki

chave de aço
fonte
Melhor abordagem IMO.
Takkat
E os arquivos sem extensão? FWIW, em zsh érm *~*.pdf
Emil Jerabek
11
Eu colocaria o ponto dentro dos parênteses.
Dennis Williamson
4
Ah, o asterisco também deve ir para dentro: !(*.py). Além disso, presumivelmente, se o OP desejar apenas arquivos ".pdf" restantes, os arquivos sem extensões também deverão ser excluídos e não ignorados.
Dennis Williamson
11
Essa abordagem é mais simples e mais limpa do que a resposta aceita.
Peter Peter
18

Excluir para a lixeira :

$ cd <the directory you want>
$ gvfs-trash !(*.pdf)

Ou via mvcomando (mas dessa maneira você não pode restaurá-lo a partir do Lixo, pois ele não registra informações de .trashinfo, isso significa que você moveu seus arquivos para um destino onde estão os seguintes).

mv !(*.pdf) ~/.local/share/Trash/files
αғsнιη
fonte
6
Essa abordagem é muito mais segura do que usar diretamente rm.
Seth
14

A abordagem mais fácil: crie outro diretório em algum lugar (se você estiver excluindo apenas um diretório, não recursivamente, pode até ser um subdiretório); mova todos os .pdf para lá; exclua todo o resto; mover as costas do pdf; exclua o diretório intermediário.

Rápido, fácil, você pode ver exatamente o que está fazendo. Apenas verifique se o diretório intermediário está no mesmo dispositivo que o diretório que você está limpando, para que as mudanças sejam renomeadas, não cópias!

Jerry
fonte
4
+1 novamente para um comentário que faça sentido para o usuário iniciante, que quase certamente não resultará na exclusão acidental de arquivos.
trognanders
4

Use o GLOBIGNORE do bash:

GLOBIGNORE=x.pdf:a.pdf
rm *
unset GLOBIGNORE

Na página de manual do bash:

GLOBIGNORE:

            Uma lista de padrões separados por dois pontos que define o conjunto
            de nomes de arquivos a serem ignorados pela expansão do nome do caminho.

Um teste rápido:

mkdir /tmp/foooooo
cd /tmp/foooooo
touch x.pdf y.zip z.mp3 a.pdf
GLOBIGNORE=x.pdf:a.pdf
ls -1 *

Resultado:

y.zip
z.mp3
Cyrus
fonte
3

Cuidado e componha: use xargs

Aqui está uma abordagem que eu gosto, porque me permite ter muito cuidado: componha uma maneira de mostrar apenas os arquivos que quero excluir e envie-os para o rmuso xargs. Por exemplo:

  • ls me mostra tudo
  • ls | grep pdfmostra os arquivos que eu quero manter. Hmm.
  • ls | grep -v pdfmostra o oposto: tudo, exceto o que eu quero manter. Em outras palavras, mostra a lista de itens que quero excluir. Posso confirmar isso antes de fazer qualquer coisa perigosa.
  • ls | grep -v pdf | xargs rmenvia exatamente essa lista rmpara exclusão

Como eu disse, gosto principalmente dessa segurança: não é acidental rm *para mim. Duas outras vantagens:

  • É composable; você pode usar lsou findpara obter a lista inicial, como preferir. Você pode usar qualquer outra coisa que desejar no processo de restringir essa lista - outra grep, alguma awkou qualquer outra coisa. Se você precisar excluir apenas arquivos cujos nomes contenham uma cor, você poderá criar da mesma maneira.
  • Você pode usar cada ferramenta para seu objetivo principal. Eu prefiro usar findpara encontrar e rmremover, em vez de ter que lembrar que findaceita uma -deletebandeira. E se você fizer isso, novamente, poderá compor soluções alternativas; talvez em vez de rm, você possa criar um trashcomando que mova o arquivo para a lixeira (permitindo "undeletion") e canalize para ele em vez de rm. Você não precisa ter findsuporte para essa opção, apenas a utiliza.

Atualizar

Veja os comentários de @pabouk para saber como modificar isso para lidar com alguns casos extremos, como quebras de linha em nomes de arquivos, nomes de arquivos como my_pdfs.zipetc.

Nathan Long
fonte
4
Notei três problemas aqui: a) Ele excluirá qualquer arquivo que contenha pdfqualquer lugar em seu nome. --- b) Exclui arquivos PDF se alguma letra do sufixo estiver em maiúsculas. --- c) Não é uma boa ideia usar a saída de ls. Não funcionará com nomes de arquivos contendo novas linhas. Algumas implementações de lssubstituir caracteres especiais, por exemplo, tabular por ?. --- É melhor utilização: find -maxdepth 1 -print0. (não tão curto quanto ls:) ----- Para resolver a) eb) use grep -vi '\.pdf$'--- solução completa (mas apenas GNU):find -maxdepth 1 -print0 | grep -viz '\.pdf$' | xargs -0 rm
pabouk
11
Entendo que você quis dizer a solução como um processo "interativo" com várias iterações manuais, mas as verificações dificilmente serão utilizáveis ​​para longas listas de arquivos e os problemas mencionados acima podem facilitar a negligência dos erros.
Pabouk
11
@pabouk bons pontos; o mundo real sempre complica as coisas e suas correções são úteis. :) Mas ainda acho que essa abordagem geral é melhor. Se houver muitos arquivos para confirmar visualmente tudo, você poderá | head -20pelo menos ver se parece mais ou menos correto, enquanto se você apenas rm my_patternnão tiver chance de detectar um grande erro.
Nathan Long
11
Você pode encontrar mostrar os arquivos antes de excluí-los também, deixar de fora -delete e apenas usar find . -type f ! -name "*.pdf"para imprimir no console ou canalizar para menos ou um arquivo. [e depois tubo para xargs para rm se desejado como comentários de pabouk (com a -print0 | ... -0 para nomes de arquivos estranhos)]
Xen2050
3

Normalmente, resolvo esses problemas com o interpretador interativo Python:

mic@mic ~ $ python
>>> import os
>>> for f in os.listdir('.'):
...   if not f.endswith('.pdf'):
...     os.remove(f)

Pode ser mais longo que um one-liner com findor xargs, mas é extremamente resistente, e eu sei exatamente o que ele faz, sem precisar pesquisar primeiro.

ratos
fonte
Para aqueles que ficam cada vez mais nervosos com cada linha adicional, que poderia torná-lo em um:for item in [f for f in os.listdir('.') if not f.endswith('.pdf')]: os.remove(item)
Jacob Vlijm
python -c "import os; for f in os.listdir('.'): if not f.endswith('.pdf'): os.remove(f)"
mic_e
[os.remove(f) for f in os.listdir('.') if not f.endswith('.pdf')]
mic_e
legais! o segundo me dá um erro de sintaxe, não vejo o porquê.
Jacob Vlijm
estranho; funciona com python 3.4 e python 2.7 no meu sistema.
mic_e
2

melhor resposta (em comparação com a minha resposta anterior) a esta pergunta será usando o filecomando poderoso .

$ file -i abc.pdf
abc: application/pdf; charset=binary

agora seu problema:

cd <the directory you want to search in>
for var in ./*
do
if file -i "$var" | grep -q 'application/pdf\;'
then
echo "$var"
fi
done

o trabalho de forcomando é dar os arquivos no diretório atual na forma de variável $var. if-thenO comando gera os nomes dos arquivos pdf assumindo o status de saída de 0from file -i "$var" | grep -q 'application/pdf\;', ele fornecerá o status de saída 0somente se encontrar arquivos PDF.

Edward Torvalds
fonte
1
rm $(ls -lo|grep -v [Pp][Dd][Ff]$|awk '{print $7}')

Aviso! Melhor tentar primeiro

ls -l $(ls -lo|grep -v [Pp][Dd][Ff]$|awk '{print $7}')
Martín-Blas Pérez Pinilla
fonte
2
Ugh, isso é falho de várias maneiras: smallo.ruhr.de/award.html#ls , smallo.ruhr.de/award.html#grep e ignora totalmente os nomes de arquivos com espaço em branco ou caracteres especiais.
David Foerster
11
Você realmente deve usar -icom greppara a correspondência de maiúsculas e minúsculas.
Muru
1
rm -i -- !(*@(a|x).pdf)

Leia como, remova todos os arquivos que não são a.pdfou x.pdf.

Isso funciona, fazendo uso de 2 globs estendidos, o exterior !()para negar o glob contido que se exige que o glob deve corresponder a um ou mais aou xpadrões antes do .pdfsufixo. Veja glob # extglob .

$ ls -a
.dotfile1 .dotfile2 a.pdf x.pdf y.zip z.mp3

$ echo -- !(a.pdf)
-- x.pdf y.zip z.mp3

$ echo -- !(x.pdf)
-- a.pdf y.zip z.mp3

$ echo -- !(a.pdf|x.pdf)
-- y.zip z.mp3

$ echo -- !(@(a|x).pdf)   # NOTE.that this matches the .dotfiles* as well
-- . .. .dotfile1 .dotfile2 y.zip z.mp3

$ echo -- !(*@(a|x).pdf)  # but this doesn't
-- y.zip z.mp3

$ echo rm -i -- !(*@(a|x).pdf)
rm -i -- y.zip z.mp3
shalomb
fonte
1

maneira shell portátil

$ ksh -c 'for i in ./*; do case $i in *.pdf)continue;; *)rm "$i";; esac;done'

Praticamente POSIX e compatível com qualquer shell Bourne-style ( ksh, bash, dash). Adequado para scripts portáteis e quando você não pode usar basho globbing de shell estendido.

perl:

$ perl -le 'opendir(my $d,"."); foreach my $f (grep(-f && !/.pdf/ , readdir($d))){unlink $f};closedir $d'                                                             

Ou um pouco mais limpo:

$ perl -le 'opendir(my $d,"."); map{ unlink $_ } grep(-f "./$_" && !/.pdf/ , readdir($d));closedir $d'

python alternativo

python -c 'import os;map(lambda x: os.remove(x), filter(lambda x: not x.endswith(".pdf"),os.listdir(".")))'
Sergiy Kolodyazhnyy
fonte
0

Cuidado com o que você está excluindo!

Uma maneira segura de testá-lo antes de tentar excluir é testá-lo primeiro ls, pois alguns comportamentos não capturados podem excluir arquivos indesejados. E você pode fazer isso diretamente fora do diretório. lsé semelhante a rm, então:

ls sub/path/to/files/!(*.pdf)

Isto irá listar

y.zip
z.mp3

E agora você pode ver o que está excluindo e pode excluí-los com segurança:

rm sub/path/to/files/!(*.pdf)

E é isso. Você pode usar curinga *para ser mais seletivo, como manter apenas os documentos do curso de programação:

rm sub/path/to/files/!(*programming*)
KeitelDOG
fonte