Remova todos os arquivos / diretórios, exceto um arquivo

244

Eu tenho um diretório que contém um grande número de arquivos. Quero excluir todos os arquivos, exceto o arquivo.txt. Como eu faço isso?

Existem muitos arquivos para remover os indesejados individualmente e seus nomes são muito diversos para serem usados ​​* para removê-los todos, exceto esse arquivo.

Alguém sugeriu o uso

rm !(file.txt)

Mas isso não funciona. Retorna:

Badly placed ()'s 

Meu SO é o Scientific Linux 6.

Alguma ideia?

Kantura
fonte
13
mova o que você deseja manter e depois os outros?
Olivier Dulac
5
@OlivierDulac Somos as únicas duas pessoas nesta pergunta que não estão pensando demais na questão?
Shadur 5/09/14
1
@shadur: bem, eu posso me relacionar com eles: os oneliners são atraentes ... ^^
Olivier Dulac
1
Obrigado, e sim, eu estava procurando uma solução de uma linha. É muito demorado para continuar movendo arquivos, pois tenho que fazer isso com bastante frequência.
Kantura 9/09/14

Respostas:

288

POSIXly:

find . ! -name 'file.txt' -type f -exec rm -f {} +

irá remover todos os arquivos regulares (recursivamente, incluindo os ocultos), exceto file.txt. Para remover diretórios, mude -type fpara -type de adicione a -ropção a rm.

Em bash, para usar rm -- !(file.txt), você deve ativar o extglob :

$ shopt -s extglob 
$ rm -- !(file.txt)

(ou chamando bash -O extglob)

Observe que extglobsó funciona na bashfamília shell Korn. E o uso rm -- !(file.txt)pode causar um Argument list too longerro.

Em zsh, você pode usar ^para negar padrão com o extendedglob ativado:

$ setopt extendedglob
$ rm -- ^file.txt

ou usando a mesma sintaxe com kshe bashcom as opções ksh_globe no_bare_glob_qualativado.

cuonglm
fonte
9
Especificar o diretório é uma boa prática (caminho completo, neste caso? Ou talvez adicione um aviso aqui de que este comando exclui todos os arquivos iniciados no diretório de trabalho atual ?). Eu também costumo escrever qualquer exemplo, com rmtão echo rmvez, e pedir às pessoas para levar apenas o eco quando eles são realmente certeza de que ele vai fazer o que eles querem. Fora isso, +1 para obter uma resposta completa
Olivier Dulac 05/09
11
Eu sugiro que -deleteem vez de -exec, mais curto e mais fácil de lembrar
Izkata
6
@ Izkata: -deletenão é definido pelo POSIX.
cuonglm
5
Como excluir lista de arquivos?
Meysam
5
@Meysam - veja minha resposta para uma solução que manipulará uma lista de arquivos. Caso contrário, com a findsolução do Gnouc, você pode fazer ! \( -name one_file -o -name two_file \)e assim por diante.
mikeserv
112

Outra tomada em uma direção diferente (se não houver espaços nos nomes dos arquivos)

ls | grep -v file.txt | xargs rm

ou (funciona mesmo se houver espaços nos nomes dos arquivos)

ls | grep -v file.txt | parallel rm

de man grep:

 -v, --invert-match
          Invert the sense of matching, to select non-matching lines.  (-v is specified by POSIX)
Sebastian
fonte
2
Ele não funcionará se os nomes de arquivo tem um espaço ...
Matteo
1
Ciao @Matteo, ele funciona para arquivos com espaços também, mas você precisa colocar o padrão grep entre aspas, por exemplo ls | grep -v "a file with spaces.bin" | xargs rm. Essa é a grepsintaxe normal .
8133 Sebastian
1
@ Sebastian O problema não é o grepmas rm. rmreceberá uma lista de argumentos separados por espaço. Tente touch 'a b'; touch 'c d'; ls | grep -v 'a b' | xargs rm: você terá rm: c: No such file or directoryerm: d: No such file or directory
Matteo
1
Sim, este é um problema comum. Veja as opções -print0dentro finde -0dentro xargs. Aqui está uma solução alternativa, mas é um pouco inconveniente. find . -maxdepth 1 -type f | grep -v 'a b' | tr '\n' '\0' | xargs -0 rm. By the way, gnu parallelalças isso muito bem (eu quase sempre usá-lo como um substituto para xargs:ls | grep -v 'a b' | parallel rm
Sebastian
1
Não são apenas os espaços, são todos os espaços em branco e as novas linhas, mas também estão citando caracteres (simples, aspas duplas e barra invertida) e nomes de arquivos começando com -.
Stéphane Chazelas 30/08/16
32

Mantenha uma cópia, exclua tudo, restaure a cópia:

{   rm -rf *
    tar -x
} <<TAR
$(tar -c $one_file)
TAR

Em uma linha:

{ rm -rf *; tar -x; } <<< $(tar -c $one_file)

Mas isso requer um shell que suporte as strings here.

mikeserv
fonte
10
Isso é algo alucinante.
vschum 5/09/14
2
@ Derek - por favor, veja a edição.
mikeserv
2
Mas se isso for interrompido no meio do caminho, ou se algo der errado, o arquivo desaparecerá.
kasperd
2
@kasperd - não. Somente se o shell pai morrer entre o tempo em que é executado rme tar -x. rmpode falhar o quanto quiser.
mikeserv
2
Não é mais eficiente movê-lo para outro diretório e movê-lo de volta? Não precisamos lidar com o conteúdo do arquivo, apenas com seu caminho.
Leonbloy
23

você está pensando demais nisso.

cd ..
mv fulldir/file.txt /tmp/
rm -rf fulldir
mkdir fulldir
mv /tmp/file.txt fulldir/

Feito.

EDIT Na verdade, mais fácil:

cd ..
ln fulldir/file.txt ./
rm -rf fulldir
mkdir -p fulldir
mv file.txt fulldir/
Shadur
fonte
3
é a mesma coisa que minha resposta faz. excede pt não perde nenhuma permissão no diretório
mikeserv
6
Se for um arquivo grande em um sistema de arquivos separado de / tmp e você estiver tentando remover tudo da raiz do sistema de arquivos para baixo, movê-lo para um local seguro pode não ser uma opção.
Johnny
1
Esta é definitivamente a resposta mais simples.
Ben Liyanage
17

No meu sistema operacional Scientific Linux 6, isso funciona:

shopt -s extglob
rm !(file.txt)

Eu também tenho o Debian 32bit instalado em uma máquina virtual. O acima não funciona, mas o seguinte:

find . -type f ! -name 'file.txt' -delete
Kantura
fonte
1
Você quer dizer que a findsolução que dei não funcionou?
cuonglm
"não funcionou" ou "não está funcionando". Sim, sua solução de localização também funciona. Obrigado.
Kantura
1
Você pode fazer mais detalhes? Como "não funcionou Ele não remove outros arquivos, ou retirá-lo? file.txtOu qualquer outra coisa?
cuonglm
Eu quis dizer que, no sistema operacional Debian "$ rm! (File.txt)" retorna "Mal colocado () 's". Então, tentei "$ shopt -s extglob", mas ele retornou: "shopt: comando não encontrado". Então eu tentei a solução "encontrar". Isso funciona.
Kantura
1
Ah, é claro que não funciona. shoptnão é um tcshbuiltin.
cuonglm
10

Use em rm !("file.txt")vez derm !(file.txt)

www.wishinfo.net
fonte
4
Isso não faz absolutamente nenhuma diferença. O problema aqui era que o OP estava 1) sem usar um shell que suporta esse formato e 2) mesmo no bash, é necessário ativá-lo shopt -s extglob. De qualquer forma, citar um nome de arquivo simples como esse não faria diferença.
terdon
1
Para manter apenas Utils fazer - rm-rf ( "utils")!
James jithin
7

Apenas para dar uma resposta diferente, você pode usar o comportamento padrão de rmque ele não excluirá pastas:

mkdir tmp && mv file.txt tmp  # create tmp dir and move files there
rm                            # delete all other files
mv tmp/* . && rm -rf tmp      # move all files back and delete tmp dir
Nithin
fonte
1

Acho que essa abordagem é muito simples, funciona e não requer extensões especiais (que eu saiba!)

ls --hide=file.txt | xargs rm
2 bits
fonte